zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 736d7d8ee8156729367d21f01934f072cadd38d0
parent 098b9c574bc9ff4d43e93592c0eee6f56c70553e
Author: Mark McCurry <[email protected]>
Date:   Sun,  1 Sep 2019 10:37:26 -0400

Merge pull request #33 from zynaddsubfx/dead-backend-fixes

Fix crash on changing master while realtime audio processing thread is inactive
Diffstat:
Msrc/Misc/Master.cpp | 71++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/Misc/Master.h | 12+++++++++---
Msrc/Misc/MiddleWare.cpp | 14+++++++++++---
3 files changed, 68 insertions(+), 29 deletions(-)

diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -787,17 +787,26 @@ Master::Master(const SYNTH_T &synth_, Config* config) } bool Master::applyOscEvent(const char *msg, float *outl, float *outr, - bool offline, bool nio, DataObj& d, int msg_id) + bool offline, bool nio, DataObj& d, int msg_id, + Master* master_from_mw) { if(!strcmp(msg, "/load-master")) { - Master *this_master = this; + Master *this_master = master_from_mw ? master_from_mw : this; Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data; + // This can not fail anymore, but just to be sure... + assert(new_master != this_master); + + /* + * WARNING: Do not use anything from "this" below, use "this_master" + */ + if(!offline) new_master->AudioOut(outl, outr); if(nio) Nio::masterSwap(new_master); - if (hasMasterCb()) - mastercb(mastercb_ptr, new_master); + if (this_master->hasMasterCb()) { + this_master->mastercb(this_master->mastercb_ptr, new_master); + } bToU->write("/free", "sb", "Master", sizeof(Master*), &this_master); return false; } else if(!strcmp(msg, "/switch-master")) { @@ -1132,30 +1141,46 @@ void dump_msg(const char* ptr, std::ostream& os = std::cerr) #endif int msg_id=0; -bool Master::runOSC(float *outl, float *outr, bool offline) +bool Master::runOSC(float *outl, float *outr, bool offline, + Master* master_from_mw) { - //Handle user events - char loc_buf[1024]; - DataObj d{loc_buf, 1024, this, bToU}; - memset(loc_buf, 0, sizeof(loc_buf)); - - int events = 0; - for(; uToB && uToB->hasNext() && events < 100; ++msg_id, ++events) + // the following block is only ever entered by 1 thread at a time + // other threads have to ignore it + if(!run_osc_in_use.exchange(true)) // exchange returns value before call { - const char *msg = uToB->read(); - if(! applyOscEvent(msg, outl, outr, offline, true, d, msg_id) ) - return false; - } + /* + * WARNING: Do not return without "run_osc_in_use.store(false)" + */ - if(automate.damaged) { - d.broadcast("/damage", "s", "/automate/"); - automate.damaged = 0; - } + //Handle user events + char loc_buf[1024]; + DataObj d{loc_buf, 1024, this, bToU}; + memset(loc_buf, 0, sizeof(loc_buf)); + + int events = 0; + for(; uToB && uToB->hasNext() && events < 100; ++msg_id, ++events) + { + const char *msg = uToB->read(); + if(! applyOscEvent(msg, outl, outr, offline, true, d, msg_id, + master_from_mw) ) + { + run_osc_in_use.store(false); + return false; + } + } - if(events>1 && false) - fprintf(stderr, "backend: %d events per cycle\n",events); + if(automate.damaged) { + d.broadcast("/damage", "s", "/automate/"); + automate.damaged = 0; + } - return true; + if(events>1 && false) + fprintf(stderr, "backend: %d events per cycle\n",events); + + run_osc_in_use.store(false); + return true; + } + else { return true; /* = no new master */ } } /* diff --git a/src/Misc/Master.h b/src/Misc/Master.h @@ -16,6 +16,7 @@ #define MASTER_H #include "../globals.h" #include "Microtonal.h" +#include <atomic> #include <rtosc/automations.h> #include <rtosc/savefile.h> @@ -120,7 +121,8 @@ class Master void vuUpdate(const float *outl, const float *outr); //Process a set of OSC events in the bToU buffer - bool runOSC(float *outl, float *outr, bool offline=false); + bool runOSC(float *outl, float *outr, bool offline=false, + Master* master_from_mw = nullptr); /**Audio Output*/ bool AudioOut(float *outl, float *outr) REALTIME; @@ -215,6 +217,8 @@ class Master uint32_t last_beat = 0; uint32_t last_ack = 0; private: + std::atomic<bool> run_osc_in_use = { false }; + float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; float sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX]; int keyshift; @@ -234,10 +238,12 @@ class Master //Used by loadOSC and saveOSC int loadOSCFromStr(const char *file_content, rtosc::savefile_dispatcher_t* dispatcher); - //applyOscEvent with a DataObj parameter + //!applyOscEvent with a DataObj parameter + //!@return false iff master has been changed bool applyOscEvent(const char *event, float *outl, float *outr, bool offline, bool nio, - class DataObj& d, int msg_id = -1); + class DataObj& d, int msg_id = -1, + Master* master_from_mw = nullptr); }; class master_dispatcher_t : public rtosc::savefile_dispatcher_t diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp @@ -578,6 +578,7 @@ public: //Update resource locator table updateResources(m); + previous_master = master; master = m; //Give it to the backend and wait for the old part to return for @@ -696,10 +697,13 @@ public: heartBeat(master); - //XXX This might have problems with a master swap operation if(offline) - master->runOSC(0,0,true); - + { + //pass previous master in case it will have to be freed + //similar to previous_master->runOSC(0,0,true) + //but note that previous_master could have been freed already + master->runOSC(0,0,true, previous_master); + } } @@ -741,6 +745,10 @@ public: //this assumption is broken Master *master; + //The master before the last load operation, if any + //Only valid until freed + Master *previous_master = nullptr; + //The ONLY means that any chunk of UI code should have for interacting with the //backend Fl_Osc_Interface *osc;