diff --git a/source/projects/chuck/core/chuck_dl.cpp b/source/projects/chuck/core/chuck_dl.cpp index 490311d..8d9b19e 100644 --- a/source/projects/chuck/core/chuck_dl.cpp +++ b/source/projects/chuck/core/chuck_dl.cpp @@ -825,6 +825,16 @@ Chuck_DL_MainThreadHook * CK_DLL_CALL ck_create_main_thread_hook( Chuck_DL_Query return new Chuck_DL_MainThreadHook( hook, quit, bindle, query->carrier() ); } +//----------------------------------------------------------------------------- +// name: ck_register_callback_on_shutdown() | 1.5.2.5 (ge) added +// desc: register a callback function to be called on host exit +// (both natural or SIGINT) +//----------------------------------------------------------------------------- +void CK_DLL_CALL ck_register_callback_on_shutdown( Chuck_DL_Query * query, f_callback_on_shutdown cb, void * bindle ) +{ + // register + query->vm()->register_callback_on_shutdown( cb, bindle ); +} //----------------------------------------------------------------------------- // name: ck_register_shreds_watcher() @@ -1351,6 +1361,7 @@ Chuck_DL_Query::Chuck_DL_Query( Chuck_Carrier * carrier, Chuck_DLL * dll ) create_main_thread_hook = ck_create_main_thread_hook; register_shreds_watcher = ck_register_shreds_watcher; // 1.5.1.5 (ge & andrew) unregister_shreds_watcher = ck_unregister_shreds_watcher; // 1.5.1.5 (ge & andrew) + register_callback_on_shutdown = ck_register_callback_on_shutdown; // 1.5.2.5 (ge) m_carrier = carrier; dll_ref = dll; // 1.5.1.3 (ge) added diff --git a/source/projects/chuck/core/chuck_dl.h b/source/projects/chuck/core/chuck_dl.h index dc45077..55e03c3 100644 --- a/source/projects/chuck/core/chuck_dl.h +++ b/source/projects/chuck/core/chuck_dl.h @@ -63,7 +63,7 @@ #define CK_DLL_VERSION_MAJOR (10) // minor API version: revisions // minor API version of chuck must >= API version of chugin -#define CK_DLL_VERSION_MINOR (1) +#define CK_DLL_VERSION_MINOR (2) #define CK_DLL_VERSION_MAKE(maj,min) ((t_CKUINT)(((maj) << 16) | (min))) #define CK_DLL_VERSION_GETMAJOR(v) (((v) >> 16) & 0xFFFF) #define CK_DLL_VERSION_GETMINOR(v) ((v) & 0xFFFF) @@ -340,6 +340,8 @@ typedef t_CKBOOL (CK_DLL_CALL * f_tock)( Chuck_Object * SELF, Chuck_UAna * UANA, typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadhook)( void * bindle ); // "main thread" quit (stop running hook) typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadquit)( void * bindle ); +// callback function, called on host shutdown +typedef void (CK_DLL_CALL * f_callback_on_shutdown)( void * bindle ); // shreds watcher callback typedef void (CK_DLL_CALL * f_shreds_watcher)( Chuck_VM_Shred * SHRED, t_CKINT CODE, t_CKINT PARAM, Chuck_VM * VM, void * BINDLE ); // type instantiation callback @@ -414,6 +416,8 @@ typedef void (CK_DLL_CALL * f_add_ugen_funcf_auto_num_channels)( Chuck_DL_Query typedef t_CKBOOL (CK_DLL_CALL * f_end_class)( Chuck_DL_Query * query ); // create main thread hook- used for executing a "hook" function in the main thread of a primary chuck instance typedef Chuck_DL_MainThreadHook * (CK_DLL_CALL * f_create_main_thread_hook)( Chuck_DL_Query * query, f_mainthreadhook hook, f_mainthreadquit quit, void * bindle ); +// register a callback to be called on host shutdown, e.g., for chugin cleanup +typedef void (CK_DLL_CALL * f_register_callback_on_shutdown)( Chuck_DL_Query * query, f_callback_on_shutdown cb, void * bindle ); // register a callback function to receive notification from the VM about shreds (add, remove, etc.) typedef void (CK_DLL_CALL * f_register_shreds_watcher)( Chuck_DL_Query * query, f_shreds_watcher cb, t_CKUINT options, void * bindle ); // unregister a shreds notification callback @@ -589,6 +593,14 @@ struct Chuck_DL_Query // ------------- f_create_main_thread_hook create_main_thread_hook; +public: + // ------------- + // register a function to be run on host shutdown; this can be used + // for chugin cleanup when the host (chuck, miniAudicle, etc.) exits + // including on SIGINT (ctrl-c termination) | added 1.5.2.5 (ge) + // ------------- + f_register_callback_on_shutdown register_callback_on_shutdown; + public: // ------------- // register callback to be invoked by chuck host at various diff --git a/source/projects/chuck/core/chuck_vm.cpp b/source/projects/chuck/core/chuck_vm.cpp index 6732894..957a6b2 100644 --- a/source/projects/chuck/core/chuck_vm.cpp +++ b/source/projects/chuck/core/chuck_vm.cpp @@ -471,6 +471,9 @@ t_CKBOOL Chuck_VM::shutdown() // set state m_init = FALSE; + // notify registered callbacks we are shutting down | 1.5.2.5 (ge) added + notify_callbacks_on_shutdown(); + // relockdown (added 1.3.6.0) // REFACTOR-2017: TODO -- remove once made per-VM Chuck_VM_Object::lock_all(); @@ -1431,6 +1434,52 @@ Chuck_VM_Shred * Chuck_VM::get_current_shred() const +//----------------------------------------------------------------------------- +// name: register_callback_on_shutdown() +// cesc: register a callback to be called on VM shutdown | 1.5.2.5 (ge) added +//----------------------------------------------------------------------------- +void Chuck_VM::register_callback_on_shutdown( f_callback_on_shutdown cb, void * bindle ) +{ + // check + if( !cb ) return; + + // ensure no duplicates + list::iterator it = std::find( m_callbacks_on_shutdown.begin(), + m_callbacks_on_shutdown.end(), cb ); + // add if not already preset + if( it == m_callbacks_on_shutdown.end() ) + { + // append + m_callbacks_on_shutdown.push_back( Chuck_VM_Callback_On_Shutdown(cb, bindle) ); + } +} + + + + +//----------------------------------------------------------------------------- +// name: notify_callbacks_on_shutdown() +// desc: notify callbacks on VM shutdown | 1.5.2.5 (ge) added +//----------------------------------------------------------------------------- +void Chuck_VM::notify_callbacks_on_shutdown() +{ + // the function to call eventually + f_callback_on_shutdown f = NULL; + // iterator + list::iterator it; + // iterate + for( it = m_callbacks_on_shutdown.begin(); it != m_callbacks_on_shutdown.end(); it++ ) + { + // the function + f = (*it).cb; + // call it with user data + f( (*it).userdata ); + } +} + + + + //----------------------------------------------------------------------------- // name: notify_watchers() // desc: notify watchers for a particular subscription | 1.5.1.5 diff --git a/source/projects/chuck/core/chuck_vm.h b/source/projects/chuck/core/chuck_vm.h index 855d514..16ac45b 100644 --- a/source/projects/chuck/core/chuck_vm.h +++ b/source/projects/chuck/core/chuck_vm.h @@ -412,6 +412,33 @@ struct Chuck_VM_Status : public Chuck_Object +//----------------------------------------------------------------------------- +// name: struct Chuck_VM_Callback_On_Shutdown +// desc: an entry for a callback to be called on VM shutdown +//----------------------------------------------------------------------------- +struct Chuck_VM_Callback_On_Shutdown +{ + // function pointer to call + f_callback_on_shutdown cb; + // user data + void * userdata; + + // constructor + Chuck_VM_Callback_On_Shutdown( f_callback_on_shutdown f = NULL, void * data = NULL ) + : cb(f), userdata(data) { } + + // copy constructor + Chuck_VM_Callback_On_Shutdown( const Chuck_VM_Callback_On_Shutdown & other ) + : cb(other.cb), userdata(other.userdata) { } + + // == + bool operator ==( const Chuck_VM_Callback_On_Shutdown & other ) + { return this->cb == other.cb; } +}; + + + + //----------------------------------------------------------------------------- // name: struct Chuck_VM_Shreduler // desc: a ChucK shreduler shredules shreds @@ -657,6 +684,14 @@ struct Chuck_VM : public Chuck_Object // remove shreds watcher callback | 1.5.1.5 void remove_watcher( f_shreds_watcher cb ); +public: + // register a callback to be called on VM shutdown | 1.5.2.5 (ge) added + void register_callback_on_shutdown( f_callback_on_shutdown cb, void * bindle ); + +protected: + // notify callbacks on VM shutdown | 1.5.2.5 (ge) added + void notify_callbacks_on_shutdown(); + //----------------------------------------------------------------------------- // data //----------------------------------------------------------------------------- @@ -733,6 +768,10 @@ struct Chuck_VM : public Chuck_Object std::list m_shreds_watchers_remove; std::list m_shreds_watchers_suspend; std::list m_shreds_watchers_activate; + +protected: + // 1.5.2.5 (ge) on major VM events callbacks + std::list m_callbacks_on_shutdown; }; diff --git a/source/projects/chugins/Fauck/Faust.cpp b/source/projects/chugins/Fauck/Faust.cpp index e87b74c..f0949dc 100644 --- a/source/projects/chugins/Fauck/Faust.cpp +++ b/source/projects/chugins/Fauck/Faust.cpp @@ -171,7 +171,7 @@ std::string getPathToFaustLibraries() { #ifdef NOBUNDLE const char* myDLLPath = getMyDLLPath(); std::filesystem::path p = std::filesystem::path(myDLLPath).parent_path().parent_path() / "faust" ; - std::cout << "path: " << p << std::endl; + std::cout << "faust library path: " << p << std::endl; return p.string(); #else // look for faustlibraries inside the bundle @@ -851,6 +851,81 @@ class Faust }; +//----------------------------------------------------------------------------- +// name: class Faucktory | v0.2.0 added (ge) +// desc: a fauck factory for globally keep track of open Faust instances +// used as a date structure of "smart pointers" to ensure each is +// closed properly; currently, for example, an abrupt closing in +// ChucK (e.g., ctrl-c) or application closing (miniAudicle, Max, etc.) +// does not cleanup live objects, leading to crashes-on-exit like: +// ``` +// libc++abi: terminating due to uncaught exception of type +// std::__1::system_error: recursive_mutex lock failed: Invalid argument +// zsh: abort +// ``` +//----------------------------------------------------------------------------- +class Faucktory +{ +public: + // destructor | to be called on exit + ~Faucktory() { cleanup(); } + + // add a Faust object to track + void add( Faust * fck ) + { + // insert + faucks[fck] = fck; + } + // remove a Faust object + void remove( Faust * fck ) + { + // count + if( faucks.count(fck) == 0 ) return; + // remove from map + faucks.erase( fck ); + } + // cleanup all tracked Faust objects + void cleanup() + { + std::map::iterator it; + for( it = faucks.begin(); it != faucks.end(); it++ ) + { + // get the pointer + Faust * fck = it->second; + // clean it up + if( fck ) fck->clear(); + } + // clear the map entries + faucks.clear(); + } + +public: // singleton API + static Faucktory * instance() + { + // instance + if( !o_faucktory ) { o_faucktory = new Faucktory(); } + return o_faucktory; + } + +protected: + std::map faucks; + +private: // enforce singleton + Faucktory() { } + static Faucktory * o_faucktory; +}; + +// static global faucktory | v0.2.0 (ge) added +Faucktory * Faucktory::o_faucktory = NULL; + +// callback to be called on host shutdown (so we can clean up) +static void cb_on_host_shutdown( void * bindle ) +{ + // explcitly call cleanup (in case of SIGINT, global object dtors doesn't run) + Faucktory::instance()->cleanup(); +} + + //----------------------------------------------------------------------------- // info function: ChucK calls this when loading/probing the chugin // NOTE: please customize these info fields below; they will be used for @@ -858,7 +933,7 @@ class Faust //----------------------------------------------------------------------------- CK_DLL_INFO( Faust ) { - QUERY->setinfo( QUERY, CHUGIN_INFO_CHUGIN_VERSION, "v0.0.1" ); + QUERY->setinfo( QUERY, CHUGIN_INFO_CHUGIN_VERSION, "v0.2.0" ); QUERY->setinfo( QUERY, CHUGIN_INFO_AUTHORS, "Ge Wang, Romain Michon, David Braun" ); QUERY->setinfo( QUERY, CHUGIN_INFO_DESCRIPTION, "A chugin which dynamically compiles and executes FAUST code via LLVM." ); QUERY->setinfo( QUERY, CHUGIN_INFO_URL, "https://github.com/ccrma/fauck" ); @@ -876,6 +951,9 @@ CK_DLL_QUERY( Faust ) // hmm, don't change this... QUERY->setname(QUERY, "Faust"); + // register a callback to be called upon host shutdown (so we can clean up) + QUERY->register_callback_on_shutdown(QUERY, cb_on_host_shutdown, NULL); + // begin the class definition // can change the second argument to extend a different ChucK class QUERY->begin_class(QUERY, "Faust", "UGen"); @@ -1006,6 +1084,9 @@ CK_DLL_CTOR(faust_ctor) // store the pointer in the ChucK object member OBJ_MEMBER_INT(SELF, faust_data_offset) = (t_CKINT) faust_obj; + + // add to global registry + Faucktory::instance()->add( faust_obj ); } // implementation for the destructor @@ -1014,6 +1095,9 @@ CK_DLL_DTOR(faust_dtor) // get our c++ class pointer Faust * faust_obj = (Faust *) OBJ_MEMBER_INT(SELF, faust_data_offset); + // remove from global registry + Faucktory::instance()->remove( faust_obj ); + // clean up (this macro tests for NULL, deletes, and zeros out the variable) CK_SAFE_DELETE( faust_obj ); // set the data field to 0 diff --git a/source/projects/chugins/chuck/include/chugin.h b/source/projects/chugins/chuck/include/chugin.h index 908d5fb..3b4d354 100644 --- a/source/projects/chugins/chuck/include/chugin.h +++ b/source/projects/chugins/chuck/include/chugin.h @@ -115,7 +115,7 @@ // 1.5.0.0 (ge) | moved to chuck.h for at-a-glance visibility // 1.5.2.0 (ge) | moved to chuck_def.h for chugins headers streamlining //----------------------------------------------------------------------------- -#define CHUCK_VERSION_STRING "1.5.2.4 (chai)" +#define CHUCK_VERSION_STRING "1.5.2.5-dev (chai)" //----------------------------------------------------------------------------- @@ -1158,7 +1158,7 @@ struct a_Program_ { a_Section section; a_Program next; uint32_t line; uint32_t w #define CK_DLL_VERSION_MAJOR (10) // minor API version: revisions // minor API version of chuck must >= API version of chugin -#define CK_DLL_VERSION_MINOR (1) +#define CK_DLL_VERSION_MINOR (2) #define CK_DLL_VERSION_MAKE(maj,min) ((t_CKUINT)(((maj) << 16) | (min))) #define CK_DLL_VERSION_GETMAJOR(v) (((v) >> 16) & 0xFFFF) #define CK_DLL_VERSION_GETMINOR(v) ((v) & 0xFFFF) @@ -1435,6 +1435,8 @@ typedef t_CKBOOL (CK_DLL_CALL * f_tock)( Chuck_Object * SELF, Chuck_UAna * UANA, typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadhook)( void * bindle ); // "main thread" quit (stop running hook) typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadquit)( void * bindle ); +// callback function, called on host shutdown +typedef void (CK_DLL_CALL * f_callback_on_shutdown)( void * bindle ); // shreds watcher callback typedef void (CK_DLL_CALL * f_shreds_watcher)( Chuck_VM_Shred * SHRED, t_CKINT CODE, t_CKINT PARAM, Chuck_VM * VM, void * BINDLE ); // type instantiation callback @@ -1509,6 +1511,8 @@ typedef void (CK_DLL_CALL * f_add_ugen_funcf_auto_num_channels)( Chuck_DL_Query typedef t_CKBOOL (CK_DLL_CALL * f_end_class)( Chuck_DL_Query * query ); // create main thread hook- used for executing a "hook" function in the main thread of a primary chuck instance typedef Chuck_DL_MainThreadHook * (CK_DLL_CALL * f_create_main_thread_hook)( Chuck_DL_Query * query, f_mainthreadhook hook, f_mainthreadquit quit, void * bindle ); +// register a callback to be called on host shutdown, e.g., for chugin cleanup +typedef void (CK_DLL_CALL * f_register_callback_on_shutdown)( Chuck_DL_Query * query, f_callback_on_shutdown cb, void * bindle ); // register a callback function to receive notification from the VM about shreds (add, remove, etc.) typedef void (CK_DLL_CALL * f_register_shreds_watcher)( Chuck_DL_Query * query, f_shreds_watcher cb, t_CKUINT options, void * bindle ); // unregister a shreds notification callback @@ -1684,6 +1688,14 @@ struct Chuck_DL_Query // ------------- f_create_main_thread_hook create_main_thread_hook; +public: + // ------------- + // register a function to be run on host shutdown; this can be used + // for chugin cleanup on the host (chuck, miniAudicle, etc.) exits + // added 1.5.2.5 (ge) + // ------------- + f_register_callback_on_shutdown register_callback_on_shutdown; + public: // ------------- // register callback to be invoked by chuck host at various