Merge branch 'master' into nmk112
This commit is contained in:
commit
e839212aa0
41 changed files with 13138 additions and 12356 deletions
|
|
@ -23,6 +23,7 @@
|
|||
#include "engine.h"
|
||||
#include "instrument.h"
|
||||
#include "safeReader.h"
|
||||
#include "workPool.h"
|
||||
#include "../ta-log.h"
|
||||
#include "../fileutils.h"
|
||||
#ifdef HAVE_SDL2
|
||||
|
|
@ -2016,6 +2017,10 @@ bool DivEngine::isPreviewingSample() {
|
|||
return (sPreview.sample>=0 && sPreview.sample<(int)song.sample.size());
|
||||
}
|
||||
|
||||
int DivEngine::getSamplePreviewSample() {
|
||||
return sPreview.sample;
|
||||
}
|
||||
|
||||
int DivEngine::getSamplePreviewPos() {
|
||||
return sPreview.pos;
|
||||
}
|
||||
|
|
@ -3119,6 +3124,10 @@ bool DivEngine::switchMaster(bool full) {
|
|||
quitDispatch();
|
||||
initDispatch();
|
||||
}
|
||||
if (renderPool!=NULL) {
|
||||
delete renderPool;
|
||||
renderPool=NULL;
|
||||
}
|
||||
if (initAudioBackend()) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].setRates(got.rate);
|
||||
|
|
@ -3273,6 +3282,10 @@ void DivEngine::quitDispatch() {
|
|||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
isMuted[i]=0;
|
||||
}
|
||||
if (renderPool!=NULL) {
|
||||
delete renderPool;
|
||||
renderPool=NULL;
|
||||
}
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
|
|
@ -3310,6 +3323,7 @@ bool DivEngine::initAudioBackend() {
|
|||
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
|
||||
if (metroVol<0.0f) metroVol=0.0f;
|
||||
if (metroVol>2.0f) metroVol=2.0f;
|
||||
renderPoolThreads=getConfInt("renderPoolThreads",0);
|
||||
|
||||
if (lowLatency) logI("using low latency mode.");
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
#include <unordered_map>
|
||||
#include <deque>
|
||||
|
||||
class DivWorkPool;
|
||||
|
||||
#define addWarning(x) \
|
||||
if (warnings.empty()) { \
|
||||
warnings+=x; \
|
||||
|
|
@ -195,6 +197,10 @@ struct DivDispatchContainer {
|
|||
bool lowQuality, dcOffCompensation;
|
||||
double rateMemory;
|
||||
|
||||
// used in multi-thread
|
||||
int cycles;
|
||||
unsigned int size;
|
||||
|
||||
void setRates(double gotRate);
|
||||
void setQuality(bool lowQual);
|
||||
void grow(size_t size);
|
||||
|
|
@ -213,7 +219,9 @@ struct DivDispatchContainer {
|
|||
lastAvail(0),
|
||||
lowQuality(false),
|
||||
dcOffCompensation(false),
|
||||
rateMemory(0.0) {
|
||||
rateMemory(0.0),
|
||||
cycles(0),
|
||||
size(0) {
|
||||
memset(bb,0,DIV_MAX_OUTPUTS*sizeof(blip_buffer_t*));
|
||||
memset(temp,0,DIV_MAX_OUTPUTS*sizeof(int));
|
||||
memset(prevSample,0,DIV_MAX_OUTPUTS*sizeof(int));
|
||||
|
|
@ -485,6 +493,9 @@ class DivEngine {
|
|||
|
||||
size_t totalProcessed;
|
||||
|
||||
unsigned int renderPoolThreads;
|
||||
DivWorkPool* renderPool;
|
||||
|
||||
// MIDI stuff
|
||||
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
||||
|
||||
|
|
@ -714,6 +725,7 @@ class DivEngine {
|
|||
|
||||
// sample preview query
|
||||
bool isPreviewingSample();
|
||||
int getSamplePreviewSample();
|
||||
int getSamplePreviewPos();
|
||||
double getSamplePreviewRate();
|
||||
|
||||
|
|
@ -1259,6 +1271,8 @@ class DivEngine {
|
|||
metroAmp(0.0f),
|
||||
metroVol(1.0f),
|
||||
totalProcessed(0),
|
||||
renderPoolThreads(0),
|
||||
renderPool(NULL),
|
||||
curOrders(NULL),
|
||||
curPat(NULL),
|
||||
tempIns(NULL),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ const char** DivPlatformAmiga::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||
static int outL, outR, output;
|
||||
thread_local int outL, outR, output;
|
||||
for (size_t h=0; h<len; h++) {
|
||||
bool hsync=bypassLimits;
|
||||
outL=0;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const char** DivPlatformArcade::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformArcade::acquire_nuked(short** buf, size_t len) {
|
||||
static int o[2];
|
||||
thread_local int o[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
for (int i=0; i<8; i++) {
|
||||
|
|
@ -92,7 +92,7 @@ void DivPlatformArcade::acquire_nuked(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformArcade::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2151::fm_engine* fme=fm_ymfm->debug_engine();
|
||||
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ void DivPlatformGenesis::processDAC(int iRate) {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
||||
static short o[2];
|
||||
static int os[2];
|
||||
thread_local short o[2];
|
||||
thread_local int os[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
processDAC(rate);
|
||||
|
|
@ -213,7 +213,7 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<12;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<11;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ const int orderedOpsL[4]={
|
|||
#define ADDR_LR_FB_ALG 0xc0
|
||||
|
||||
void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
||||
static short o[4];
|
||||
static int os[4];
|
||||
static ymfm::ymfm_output<2> aOut;
|
||||
thread_local short o[4];
|
||||
thread_local int os[4];
|
||||
thread_local ymfm::ymfm_output<2> aOut;
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
os[0]=0; os[1]=0; os[2]=0; os[3]=0;
|
||||
|
|
@ -549,6 +549,8 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
chan[adpcmChan].freq=5461; // 4KHz
|
||||
}
|
||||
}
|
||||
if (chan[adpcmChan].freq<0) chan[adpcmChan].freq=0;
|
||||
if (chan[adpcmChan].freq>65535) chan[adpcmChan].freq=65535;
|
||||
immWrite(16,chan[adpcmChan].freq&0xff);
|
||||
immWrite(17,(chan[adpcmChan].freq>>8)&0xff);
|
||||
if (chan[adpcmChan].keyOn || chan[adpcmChan].keyOff) {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ const unsigned char visMapOPLL[9]={
|
|||
};
|
||||
|
||||
void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
||||
static int o[2];
|
||||
static int os;
|
||||
thread_local int o[2];
|
||||
thread_local int os;
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
os=0;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#define chWrite(c,a,v) rWrite(((c)<<3)+(a),v)
|
||||
|
||||
void DivPlatformSegaPCM::acquire(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const char** DivPlatformTX81Z::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformTX81Z::acquire(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2414::fm_engine* fme=fm_ymfm->debug_engine();
|
||||
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ void DivPlatformYM2203::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
||||
static int os;
|
||||
static short ignored[2];
|
||||
thread_local int os;
|
||||
thread_local short ignored[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
|
|
@ -241,7 +241,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os;
|
||||
thread_local int os;
|
||||
|
||||
ymfm::ym2203::fm_engine* fme=fm->debug_fm_engine();
|
||||
|
||||
|
|
|
|||
|
|
@ -306,8 +306,8 @@ void DivPlatformYM2608::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
static short ignored[2];
|
||||
thread_local int os[2];
|
||||
thread_local short ignored[2];
|
||||
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
|
||||
|
|
@ -419,7 +419,7 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2608::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
|
|
@ -783,6 +783,8 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
chan[15].freq=0;
|
||||
}
|
||||
}
|
||||
if (chan[adpcmBChanOffs].freq<0) chan[adpcmBChanOffs].freq=0;
|
||||
if (chan[adpcmBChanOffs].freq>65535) chan[adpcmBChanOffs].freq=65535;
|
||||
immWrite(0x109,chan[15].freq&0xff);
|
||||
immWrite(0x10a,(chan[15].freq>>8)&0xff);
|
||||
hardResetElapsed+=2;
|
||||
|
|
|
|||
|
|
@ -241,8 +241,8 @@ void DivPlatformYM2610::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
static short ignored[2];
|
||||
thread_local int os[2];
|
||||
thread_local short ignored[2];
|
||||
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
|
||||
|
|
@ -350,7 +350,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2610::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
|
|
@ -717,6 +717,8 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
} else {
|
||||
chan[adpcmBChanOffs].freq=0;
|
||||
}
|
||||
if (chan[adpcmBChanOffs].freq<0) chan[adpcmBChanOffs].freq=0;
|
||||
if (chan[adpcmBChanOffs].freq>65535) chan[adpcmBChanOffs].freq=65535;
|
||||
immWrite(0x19,chan[adpcmBChanOffs].freq&0xff);
|
||||
immWrite(0x1a,(chan[adpcmBChanOffs].freq>>8)&0xff);
|
||||
hardResetElapsed+=2;
|
||||
|
|
|
|||
|
|
@ -305,8 +305,8 @@ void DivPlatformYM2610B::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
static short ignored[2];
|
||||
thread_local int os[2];
|
||||
thread_local short ignored[2];
|
||||
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
|
||||
|
|
@ -418,7 +418,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
||||
static int os[2];
|
||||
thread_local int os[2];
|
||||
|
||||
ymfm::ym2610b::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
|
|
@ -784,6 +784,8 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
} else {
|
||||
chan[adpcmBChanOffs].freq=0;
|
||||
}
|
||||
if (chan[adpcmBChanOffs].freq<0) chan[adpcmBChanOffs].freq=0;
|
||||
if (chan[adpcmBChanOffs].freq>65535) chan[adpcmBChanOffs].freq=65535;
|
||||
immWrite(0x19,chan[adpcmBChanOffs].freq&0xff);
|
||||
immWrite(0x1a,(chan[adpcmBChanOffs].freq>>8)&0xff);
|
||||
hardResetElapsed+=2;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include "dispatch.h"
|
||||
#include "engine.h"
|
||||
#include "workPool.h"
|
||||
#include "../ta-log.h"
|
||||
#include <math.h>
|
||||
|
||||
|
|
@ -1759,6 +1760,13 @@ void DivEngine::runMidiTime(int totalCycles) {
|
|||
}
|
||||
}
|
||||
|
||||
void _runDispatch1(void* d) {
|
||||
}
|
||||
|
||||
void _runDispatch2(void* d) {
|
||||
|
||||
}
|
||||
|
||||
void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) {
|
||||
lastNBIns=inChans;
|
||||
lastNBOuts=outChans;
|
||||
|
|
@ -1788,6 +1796,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
|
||||
std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now();
|
||||
|
||||
if (renderPool==NULL) {
|
||||
unsigned int howManyThreads=song.systemLen;
|
||||
if (howManyThreads<2) howManyThreads=0;
|
||||
if (howManyThreads>renderPoolThreads) howManyThreads=renderPoolThreads;
|
||||
renderPool=new DivWorkPool(howManyThreads);
|
||||
}
|
||||
|
||||
// process MIDI events (TODO: everything)
|
||||
if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) {
|
||||
TAMidiMessage& msg=output->midiIn->queue.front();
|
||||
|
|
@ -2061,20 +2076,30 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
// 5. tick the clock and fill buffers as needed
|
||||
if (cycles<runLeftG) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
int total=(cycles*disCont[i].runtotal)/(size<<MASTER_CLOCK_PREC);
|
||||
disCont[i].acquire(disCont[i].runPos,total);
|
||||
disCont[i].runLeft-=total;
|
||||
disCont[i].runPos+=total;
|
||||
disCont[i].cycles=cycles;
|
||||
disCont[i].size=size;
|
||||
renderPool->push([](void* d) {
|
||||
DivDispatchContainer* dc=(DivDispatchContainer*)d;
|
||||
int total=(dc->cycles*dc->runtotal)/(dc->size<<MASTER_CLOCK_PREC);
|
||||
dc->acquire(dc->runPos,total);
|
||||
dc->runLeft-=total;
|
||||
dc->runPos+=total;
|
||||
},&disCont[i]);
|
||||
}
|
||||
renderPool->wait();
|
||||
runLeftG-=cycles;
|
||||
cycles=0;
|
||||
} else {
|
||||
cycles-=runLeftG;
|
||||
runLeftG=0;
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].acquire(disCont[i].runPos,disCont[i].runLeft);
|
||||
disCont[i].runLeft=0;
|
||||
renderPool->push([](void* d) {
|
||||
DivDispatchContainer* dc=(DivDispatchContainer*)d;
|
||||
dc->acquire(dc->runPos,dc->runLeft);
|
||||
dc->runLeft=0;
|
||||
},&disCont[i]);
|
||||
}
|
||||
renderPool->wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2093,8 +2118,12 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
logW("%d: size<lastAvail! %d<%d",i,size,disCont[i].lastAvail);
|
||||
continue;
|
||||
}
|
||||
disCont[i].fillBuf(disCont[i].runtotal,disCont[i].lastAvail,size-disCont[i].lastAvail);
|
||||
renderPool->push([](void* d) {
|
||||
DivDispatchContainer* dc=(DivDispatchContainer*)d;
|
||||
dc->fillBuf(dc->runtotal,dc->lastAvail,dc->size-dc->lastAvail);
|
||||
},&disCont[i]);
|
||||
}
|
||||
renderPool->wait();
|
||||
}
|
||||
|
||||
if (metroBufLen<size || metroBuf==NULL) {
|
||||
|
|
|
|||
|
|
@ -454,10 +454,11 @@ void DivEngine::registerSystems() {
|
|||
{0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}},
|
||||
});
|
||||
|
||||
EffectHandlerMap fmOPN2EffectHandlerMap={
|
||||
EffectHandlerMap fmOPN2EffectHandlerMap(fmEffectHandlerMap);
|
||||
fmOPN2EffectHandlerMap.insert({
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}},
|
||||
{0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}},
|
||||
};
|
||||
});
|
||||
|
||||
EffectHandlerMap fmOPLDrumsEffectHandlerMap(fmEffectHandlerMap);
|
||||
fmOPLDrumsEffectHandlerMap.insert({
|
||||
|
|
@ -1179,7 +1180,7 @@ void DivEngine::registerSystems() {
|
|||
|
||||
sysDefs[DIV_SYSTEM_SWAN]=new DivSysDef(
|
||||
"WonderSwan", NULL, 0x96, 0, 4, false, true, 0x171, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"developed by the same team under the Game Boy and the Virtual Boy...",
|
||||
"developed by the makers of the Game Boy and the Virtual Boy...",
|
||||
{"Wave", "Wave/PCM", "Wave", "Wave/Noise"},
|
||||
{"CH1", "CH2", "CH3", "CH4"},
|
||||
{DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE},
|
||||
|
|
|
|||
204
src/engine/workPool.cpp
Normal file
204
src/engine/workPool.cpp
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "workPool.h"
|
||||
#include "../ta-log.h"
|
||||
#include <thread>
|
||||
|
||||
void* _workThread(void* inst) {
|
||||
((DivWorkThread*)inst)->run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivWorkThread::run() {
|
||||
//std::unique_lock<std::mutex> unique(selfLock);
|
||||
DivPendingTask task;
|
||||
bool setFuckingPromise=false;
|
||||
|
||||
logV("running work thread");
|
||||
|
||||
while (true) {
|
||||
lock.lock();
|
||||
if (tasks.empty()) {
|
||||
lock.unlock();
|
||||
isBusy=false;
|
||||
if (setFuckingPromise) {
|
||||
parent->notify.set_value();
|
||||
setFuckingPromise=false;
|
||||
//std::this_thread::yield();
|
||||
}
|
||||
if (terminate) {
|
||||
break;
|
||||
}
|
||||
std::future<void> future=notify.get_future();
|
||||
future.wait();
|
||||
lock.lock();
|
||||
notify=std::promise<void>();
|
||||
promiseAlreadySet=false;
|
||||
lock.unlock();
|
||||
continue;
|
||||
} else {
|
||||
task=tasks.front();
|
||||
tasks.pop();
|
||||
lock.unlock();
|
||||
|
||||
task.func(task.funcArg);
|
||||
|
||||
int busyCount=--parent->busyCount;
|
||||
if (busyCount<0) {
|
||||
logE("oh no PROBLEM...");
|
||||
}
|
||||
if (busyCount==0) {
|
||||
setFuckingPromise=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DivWorkThread::assign(void (*what)(void*), void* arg) {
|
||||
lock.lock();
|
||||
if (tasks.size()>=30) {
|
||||
lock.unlock();
|
||||
return false;
|
||||
}
|
||||
tasks.push(DivPendingTask(what,arg));
|
||||
parent->busyCount++;
|
||||
isBusy=true;
|
||||
lock.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivWorkThread::wait() {
|
||||
if (!isBusy) return;
|
||||
}
|
||||
|
||||
bool DivWorkThread::busy() {
|
||||
return isBusy;
|
||||
}
|
||||
|
||||
void DivWorkThread::finish() {
|
||||
lock.lock();
|
||||
terminate=true;
|
||||
notify.set_value();
|
||||
lock.unlock();
|
||||
thread->join();
|
||||
}
|
||||
|
||||
bool DivWorkThread::init(DivWorkPool* p) {
|
||||
parent=p;
|
||||
try {
|
||||
thread=new std::thread(_workThread,this);
|
||||
} catch (std::system_error& e) {
|
||||
logE("could not start thread! %s",e.what());
|
||||
thread=NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivWorkPool::push(void (*what)(void*), void* arg) {
|
||||
// if no work threads, just execute
|
||||
if (!threaded) {
|
||||
what(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int tryCount=0; tryCount<count; tryCount++) {
|
||||
if (pos>=count) pos=0;
|
||||
if (workThreads[pos++].assign(what,arg)) return;
|
||||
}
|
||||
|
||||
// all threads are busy
|
||||
logW("DivWorkPool: all work threads busy!");
|
||||
what(arg);
|
||||
}
|
||||
|
||||
bool DivWorkPool::busy() {
|
||||
if (!threaded) return false;
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
if (workThreads[i].busy()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivWorkPool::wait() {
|
||||
if (!threaded) return;
|
||||
|
||||
if (busyCount==0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::future<void> future=notify.get_future();
|
||||
|
||||
// start running
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
if (!workThreads[i].promiseAlreadySet && !workThreads[i].tasks.empty()) {
|
||||
try {
|
||||
workThreads[i].lock.lock();
|
||||
workThreads[i].promiseAlreadySet=true;
|
||||
workThreads[i].notify.set_value();
|
||||
workThreads[i].lock.unlock();
|
||||
} catch (std::exception& e) {
|
||||
logE("ERROR IN THREAD SYNC! %s",e.what());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
//std::this_thread::yield();
|
||||
|
||||
// wait
|
||||
future.wait();
|
||||
|
||||
notify=std::promise<void>();
|
||||
|
||||
pos=0;
|
||||
}
|
||||
|
||||
DivWorkPool::DivWorkPool(unsigned int threads):
|
||||
threaded(threads>0),
|
||||
count(threads),
|
||||
pos(0),
|
||||
busyCount(0) {
|
||||
if (threaded) {
|
||||
workThreads=new DivWorkThread[threads];
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
if (!workThreads[i].init(this)) {
|
||||
count=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count<=0) {
|
||||
logE("DivWorkPool: couldn't start any threads! falling back to non-threaded mode.");
|
||||
delete[] workThreads;
|
||||
threaded=false;
|
||||
workThreads=NULL;
|
||||
}
|
||||
} else {
|
||||
workThreads=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DivWorkPool::~DivWorkPool() {
|
||||
if (threaded) {
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
workThreads[i].finish();
|
||||
}
|
||||
delete[] workThreads;
|
||||
}
|
||||
}
|
||||
101
src/engine/workPool.h
Normal file
101
src/engine/workPool.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _WORKPOOL_H
|
||||
#define _WORKPOOL_H
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
|
||||
#include "fixedQueue.h"
|
||||
|
||||
class DivWorkPool;
|
||||
|
||||
struct DivPendingTask {
|
||||
void (*func)(void*);
|
||||
void* funcArg;
|
||||
DivPendingTask(void (*f)(void*), void* arg):
|
||||
func(f),
|
||||
funcArg(arg) {}
|
||||
DivPendingTask():
|
||||
func(NULL),
|
||||
funcArg(NULL) {}
|
||||
};
|
||||
|
||||
struct DivWorkThread {
|
||||
DivWorkPool* parent;
|
||||
std::mutex lock;
|
||||
std::thread* thread;
|
||||
std::promise<void> notify;
|
||||
FixedQueue<DivPendingTask,32> tasks;
|
||||
std::atomic<bool> isBusy;
|
||||
bool terminate;
|
||||
bool promiseAlreadySet;
|
||||
|
||||
void run();
|
||||
bool assign(void (*what)(void*), void* arg);
|
||||
void wait();
|
||||
bool busy();
|
||||
void finish();
|
||||
|
||||
bool init(DivWorkPool* p);
|
||||
DivWorkThread():
|
||||
parent(NULL),
|
||||
isBusy(false),
|
||||
terminate(false),
|
||||
promiseAlreadySet(false) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* this class provides an implementation of a "thread pool" for executing tasks in parallel.
|
||||
* it is highly recommended to use `new` when allocating a DivWorkPool.
|
||||
*/
|
||||
class DivWorkPool {
|
||||
bool threaded;
|
||||
unsigned int count;
|
||||
unsigned int pos;
|
||||
DivWorkThread* workThreads;
|
||||
public:
|
||||
std::promise<void> notify;
|
||||
std::atomic<int> busyCount;
|
||||
|
||||
/**
|
||||
* push a new job to this work pool.
|
||||
* if all work threads are busy, this will block until one is free.
|
||||
*/
|
||||
void push(void (*what)(void*), void* arg);
|
||||
|
||||
/**
|
||||
* check whether this work pool is busy.
|
||||
*/
|
||||
bool busy();
|
||||
|
||||
/**
|
||||
* wait for all work threads to finish.
|
||||
*/
|
||||
void wait();
|
||||
|
||||
DivWorkPool(unsigned int threads=0);
|
||||
~DivWorkPool();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue