Skip to content

Commit

Permalink
support for explicit gc() trigger in Machine and Shred
Browse files Browse the repository at this point in the history
  • Loading branch information
gewang committed Dec 3, 2023
1 parent b620658 commit 36ffa22
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 69 deletions.
6 changes: 5 additions & 1 deletion src/core/chuck_instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6112,7 +6112,7 @@ void Chuck_Instr_Time_Advance::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
{
t_CKTIME *& sp = (t_CKTIME *&)shred->reg->sp;

// pop word from reg stack
// pop time value from reg stack
pop_( sp, 1 );

// check for immediate mode exception | 1.5.1.5 (ge)
Expand Down Expand Up @@ -6143,10 +6143,14 @@ void Chuck_Instr_Time_Advance::execute( Chuck_VM * vm, Chuck_VM_Shred * shred )
vm->shreduler()->shredule( shred, *sp );
// suspend
shred->is_running = FALSE;
// increment towards per-shred garbage collection | 1.5.2.0 (ge)
// NOTE 0-dur advance possible; but inc at least 1::samp
shred->gc_inc( ck_max((*sp)-shred->now,1) );

// track time advance
CK_TRACK( Chuck_Stats::instance()->advance_time( shred, *sp ) );

// push time value on stack
push_( sp, *sp );
}

Expand Down
15 changes: 15 additions & 0 deletions src/core/chuck_lang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,11 @@ t_CKBOOL init_class_shred( Chuck_Env * env, Chuck_Type * type )
func->doc = "get the calling shred's \"ancestor\" shred (i.e., the top-level shred). Returns itself if the calling shred is the top-level shred. (Related: see Shred.parent())";
if( !type_engine_import_sfun( env, func ) ) goto error;

// add gc() | 1.5.2.0 (ge) added
func = make_new_mfun( "void", "gc", shred_gc );
func->doc = "manually trigger a per-shred garbage collection pass; can be used to clean up certain UGens/objects without waiting for the shred to complete; use with care.";
if( !type_engine_import_mfun( env, func ) ) goto error;

// add examples
if( !type_engine_import_add_ex( env, "shred/spork.ck" ) ) goto error;
if( !type_engine_import_add_ex( env, "shred/spork2.ck" ) ) goto error;
Expand Down Expand Up @@ -2573,6 +2578,16 @@ CK_DLL_SFUN( shred_ancestor ) // added 1.5.2.0 (nshaheed)
RETURN->v_object = curr;
}


CK_DLL_MFUN( shred_gc ) // added 1.5.2.0 (ge)
{
// invoke manual per-shred GC pass
SHRED->gc();
}




//-----------------------------------------------------------------------------
// string API
//-----------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/core/chuck_lang.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ CK_DLL_MFUN( shred_id );
CK_DLL_MFUN( shred_yield );
CK_DLL_MFUN( shred_running );
CK_DLL_MFUN( shred_done );
CK_DLL_MFUN( shred_gc ); // added 1.5.2.0 (ge)
CK_DLL_MFUN( shred_numArgs );
CK_DLL_MFUN( shred_getArg );
CK_DLL_MFUN( shred_sourcePath ); // added 1.3.0.0
Expand Down
190 changes: 142 additions & 48 deletions src/core/chuck_vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,24 +640,23 @@ t_CKBOOL Chuck_VM::run( t_CKINT N, const SAMPLE * input, SAMPLE * output )




//-----------------------------------------------------------------------------
// name: gc
// desc: ...
//-----------------------------------------------------------------------------
void Chuck_VM::gc( t_CKUINT amount )
{
}




//-----------------------------------------------------------------------------
// name: gc
// desc: ...
// name: gc() | 1.5.2.0 (ge) added
// desc: manually trigger a VM-level garbage collection pass
//-----------------------------------------------------------------------------
void Chuck_VM::gc( )
{
// vector of shreds
vector<Chuck_VM_Shred *> shreds;
// retrieve all shreds currently in the shreduler; this includes the
// ready list, the blocked list, and the currently executing shred
m_shreduler->get_all_shreds( shreds );
// iterate through all shreds in VM
for( t_CKUINT i = 0; i < shreds.size(); i++ )
{
// manually trigger GC pass in each shred
shreds[i]->gc();
}
}


Expand Down Expand Up @@ -1715,6 +1714,10 @@ Chuck_VM_Shred::Chuck_VM_Shred()
is_immediate_mode = FALSE;
is_immediate_mode_violation = FALSE;

// garbage collection related | 1.5.2.0
m_gc_inc = 0;
m_gc_threshold = 4192; // default samps until next per-shred GC event

#ifndef __DISABLE_SERIAL__
m_serials = NULL;
#endif
Expand Down Expand Up @@ -1811,45 +1814,100 @@ t_CKBOOL Chuck_VM_Shred::initialize( Chuck_VM_Code * c,
void Chuck_VM_Shred::detach_ugens()
{
// check if we have anything in ugen map for this shred
if( m_ugen_map.size() )
if( !m_ugen_map.size() )
return;

// spencer - March 2012 (added 1.3.0.0)
// can't dealloc ugens while they are still keys to a map;
// add reference, store them in a vector, and release them after
// SPENCERTODO: is there a better way to do this????
std::vector<Chuck_UGen *> release_v;
release_v.reserve( m_ugen_map.size() );

// get iterator to our map
map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
while( iter != m_ugen_map.end() )
{
// spencer - March 2012 (added 1.3.0.0)
// can't dealloc ugens while they are still keys to a map;
// add reference, store them in a vector, and release them after
// SPENCERTODO: is there a better way to do this????
std::vector<Chuck_UGen *> release_v;
release_v.reserve( m_ugen_map.size() );

// get iterator to our map
map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
while( iter != m_ugen_map.end() )
{
// get the ugen
Chuck_UGen * ugen = iter->first;

// store ref in array for now (added 1.3.0.0)
// NOTE no need to bump reference since now ugen_map ref counts
release_v.push_back(ugen);

// make sure if ugen has an origin shred, it is this one | 1.5.1.5
assert( !ugen->originShred() || ugen->originShred() == this );
// also clear reference to this shred | 1.5.1.5
ugen->setOriginShred( NULL );
// disconnect
ugen->disconnect( TRUE );

// advance the iterator
iter++;
}
// clear map
m_ugen_map.clear();
// get the ugen
Chuck_UGen * ugen = iter->first;

// store ref in array for now (added 1.3.0.0)
// NOTE no need to bump reference since now ugen_map ref counts
release_v.push_back(ugen);

// make sure if ugen has an origin shred, it is this one | 1.5.1.5
assert( !ugen->originShred() || ugen->originShred() == this );
// also clear reference to this shred | 1.5.1.5
ugen->setOriginShred( NULL );
// disconnect
ugen->disconnect( TRUE );

// advance the iterator
iter++;
}
// clear map
m_ugen_map.clear();

// loop over vector
for( vector<Chuck_UGen *>::iterator rvi = release_v.begin();
rvi != release_v.end(); rvi++ )
{
// cerr << "RELEASE: " << (void *) *rvi<< endl;
// release it
CK_SAFE_RELEASE( *rvi );
}
// clear the release vector
release_v.clear();
}




//-----------------------------------------------------------------------------
// name: prune_ugens() | 1.5.2.0 (ge) added
// desc: manually trigger a pruning of UGens that can be safely released,
// associated with a shred, instead of waiting for the shred to finish
// NOTE this can be useful if a shred dynamically creates a lot of
// UGens without exiting
//-----------------------------------------------------------------------------
void Chuck_VM_Shred::prune_ugens()
{
// check if we have anything in ugen map for this shred
if( !m_ugen_map.size() )
return;

// ugens to release
std::vector<Chuck_UGen *> release_v;

// get iterator to our map
map<Chuck_UGen *, Chuck_UGen *>::iterator iter = m_ugen_map.begin();
while( iter != m_ugen_map.end() )
{
// get the ugen
Chuck_UGen * ugen = iter->first;

// verify
assert( ugen->refcount() > 0 );
// check for ugens with only one reference (to this shred)
if( ugen->refcount() == 1 ) release_v.push_back( ugen );

// advance the iterator
iter++;
}

// check if anything to prune
if( release_v.size() )
{
// log
EM_log( CK_LOG_FINE, "pruning '%ld' ugen(s) from shred: %x...", release_v.size(), this );

// loop over vector
for( vector<Chuck_UGen *>::iterator rvi = release_v.begin();
rvi != release_v.end(); rvi++ )
rvi != release_v.end(); rvi++ )
{
// cerr << "RELEASE: " << (void *) *rvi<< endl;
// release it
// remove from map
m_ugen_map.erase( *rvi );
// release the ugen
CK_SAFE_RELEASE( *rvi );
}
// clear the release vector
Expand All @@ -1860,6 +1918,42 @@ void Chuck_VM_Shred::detach_ugens()



//-----------------------------------------------------------------------------
// name: gc_inc() | 1.5.2.0 (ge) added
// desc: acrue towards a GC pass
//-----------------------------------------------------------------------------
void Chuck_VM_Shred::gc_inc( t_CKDUR inc )
{
// log
// EM_log( CK_LOG_FINE, "accruing '%f' towards GC on shred: %x", inc, this );

// accumulate
m_gc_inc += inc;
// check threshold
if( m_gc_inc > m_gc_threshold )
{
// invoke gc
gc();
// reset inc
m_gc_inc = 0;
}
}




//-----------------------------------------------------------------------------
// name: gc() | 1.5.2.0 (ge) added
// desc: manually trigger a pre-shred garbage collection pass
//-----------------------------------------------------------------------------
void Chuck_VM_Shred::gc()
{
this->prune_ugens();
}




//-----------------------------------------------------------------------------
// name: shutdown()
// desc: shutdown a shred
Expand Down
37 changes: 26 additions & 11 deletions src/core/chuck_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,26 @@ struct Chuck_VM_Shred : public Chuck_Object
// yield the shred in vm (without advancing time, politely yield to run
// all other shreds waiting to run at the current (i.e., 0::second +=> now;)
t_CKBOOL yield(); // 1.5.0.5 (ge) made this a function from scattered code
// add parent object reference (added 1.3.1.2)
t_CKVOID add_parent_ref( Chuck_Object * obj );
// add get shred id | 1.5.0.8 (ge)
t_CKUINT get_id() const { return this->xid; }

public:
// associate ugen with shred
t_CKBOOL add( Chuck_UGen * ugen );
// unassociate ugen with shred
t_CKBOOL remove( Chuck_UGen * ugen );
// detach all associate ugens | 1.5.1.5 (ge) added
void detach_ugens();
// clean up ugens | 1.5.2.0 (ge) added
void prune_ugens();

// add parent object reference (added 1.3.1.2)
t_CKVOID add_parent_ref( Chuck_Object * obj );
// add get shred id | 1.5.0.8 (ge)
t_CKUINT get_id() const { return this->xid; }

public:
// manually trigger a per-shred garbage collection pass | 1.5.2.0 (ge) added
void gc();
// acrue towards a GC pass
void gc_inc( t_CKDUR inc );
// affects children shreds sporked from this one
// mem is memory / call stack (for local vars)
t_CKINT childSetMemSize( t_CKINT sizeInBytes );
Expand Down Expand Up @@ -254,11 +262,6 @@ struct Chuck_VM_Shred : public Chuck_Object

// event shred is waiting on
Chuck_Event * event;
// map of ugens for the shred
std::map<Chuck_UGen *, Chuck_UGen *> m_ugen_map;
// references kept by the shred itself (e.g., when sporking member functions)
// to be released when shred is done -- added 1.3.1.2
std::vector<Chuck_Object *> m_parent_objects;

public: // id
t_CKUINT xid;
Expand All @@ -270,9 +273,17 @@ struct Chuck_VM_Shred : public Chuck_Object
Chuck_VM_Shred * prev;
Chuck_VM_Shred * next;

public:
// tracking
CK_TRACK( Shred_Stat * stat );

public:
// map of ugens for the shred
std::map<Chuck_UGen *, Chuck_UGen *> m_ugen_map;
// references kept by the shred itself (e.g., when sporking member functions)
// to be released when shred is done -- added 1.3.1.2
std::vector<Chuck_Object *> m_parent_objects;

public: // ge: 1.3.5.3
// make and push new loop counter
t_CKUINT * pushLoopCounter();
Expand Down Expand Up @@ -307,6 +318,10 @@ struct Chuck_VM_Shred : public Chuck_Object
t_CKBOOL is_immediate_mode;
t_CKBOOL is_immediate_mode_violation;

protected:
t_CKDUR m_gc_inc; // current GC increment (in samps)
t_CKDUR m_gc_threshold; // threshold (in samps) beyond which will trigger a gc()

#ifndef __DISABLE_SERIAL__
private:
// serial IO list for event synchronization
Expand Down Expand Up @@ -596,8 +611,8 @@ struct Chuck_VM : public Chuck_Object
t_CKBOOL invoke_static( Chuck_VM_Shred * shred );

public: // garbage collection
// manually trigger a VM-level garbage collection pass | 1.5.2.0 (ge) added
void gc();
void gc( t_CKUINT amount );

public: // VM message queue
// queue message to process at next VM compute block (thread-safe but not synchronous)
Expand Down
Loading

0 comments on commit 36ffa22

Please sign in to comment.