Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve GC behavior for UGens; fix SndBuf FD cleanup #478

Merged
merged 4 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ ChucK VERSIONS log
specified paths; instead the specified paths are added to the import
system user paths, and the .chug and .ck files within the specified
paths can be @imported.
(updated) improved internal garbage collection mechanism for all UGens;
now UGens are garbage collected and disconnected from all its UGen
connections, as soon as it is now longer referenced from code (previously,
this was deferred until the end of origin shred the UGen was created on,
which poses potentially significant memory build-up if a shred repeatedly
instantiated/connected UGens without exiting). Overall, this internal
update should result in improved CPU performance and memory behavior in
various scenarios.
(fixed) long-standing SndBuf issue, especially prevalent on macOS:
"System error : Too many open files." this was due a combination of
(added) --auto-load-chugin-path:<path> flag -- this behaves as
--chugin-path previously did: chugins in the specified path(s) are
auto-loaded; .ck files can be @imported
Expand Down
22 changes: 18 additions & 4 deletions src/core/chuck_instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4381,7 +4381,8 @@ t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_S
// REFACTOR-2017: added | 1.5.1.5 (ge & andrew) moved here from instantiate_...
object->setOriginVM( vm );
// set origin shred for non-ugens | 1.5.1.5 (ge & andrew) moved here from instantiate_...
if( !type->ugen_info && setShredOrigin ) object->setOriginShred( shred );
// change logic: if ugen OR setShredOrigin==TRUE | 1.5.4.2 (ge) part of #ugen-refs
if( type->ugen_info || setShredOrigin ) object->setOriginShred( shred );

// allocate virtual table
object->vtable = new Chuck_VTable;
Expand Down Expand Up @@ -4410,15 +4411,24 @@ t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_S
{
// ugen
Chuck_UGen * ugen = (Chuck_UGen *)object;
//---------------------------------------
// UGens: needs shred for auto-disconnect when shred is removed
// 1.5.1.5 (ge & andrew) moved from instantiate_and_initialize_object()
//---------------------------------------
// 1.5.4.2 (ge) revisiting the above mechanism, part of #ugen-refs
// now UGens are not ref-counted by shred, is subject to the normal GC,
// and when refcount goes to 0, will remove it self from UGen graph
//---------------------------------------
if( shred )
{
// add ugen to shred (ref-counted)
// register ugen with originShred; if the shred is preemptively removed
// (e.g., through OTF / Machine.remove()), it will trigger a ugen_detach()
// to disconnect UGens that were created on it...
// 1.5.4.2 (ge) no longer ref-counted as part of #ugen-refs
// FYI the ugen's originShred should be set already (above) for UGens
shred->add( ugen );
// add shred to ugen (ref-counted) | 1.5.1.5 (ge) was: ugen->shred = shred;
object->setOriginShred( shred );
}
//---------------------------------------
// set tick
if( type->ugen_info->tick ) ugen->tick = type->ugen_info->tick;
// added 1.3.0.0 -- tickf for multi-channel tick
Expand All @@ -4433,6 +4443,10 @@ t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_S
for( t_CKUINT i = 0; i < ugen->m_multi_chan_size; i++ )
{
// allocate ugen for each | REFACTOR-2017: added ugen->vm
// NOTE the channels currently are also detached as part of the
// origin shred's ugen_detach() routine when the shred
// is removed; as of 1.5.4.2, however, ugens are no longer
// reference-counted when added to their origin shreds
Chuck_Object * obj = instantiate_and_initialize_object(
ugen->originVM()->env()->ckt_ugen, ugen->originShred(), ugen->originVM() );
// cast to ugen
Expand Down
6 changes: 6 additions & 0 deletions src/core/chuck_lang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ t_CKBOOL init_class_ugen( Chuck_Env * env, Chuck_Type * type )
func->doc = "get the ugen's buffered operation mode.";
if( !type_engine_import_mfun( env, func ) ) goto error;

// // add attachToOriginShred
// func = make_new_mfun( "Shred", "attachToOriginShred", ugen_originShred );
// func->add_arg( "Shred", "shred" );
// func->doc = "(Use with care) by default, a UGen is attached to its origin Shred (the Shred the UGen was created on); if that Shred is removed (e.g., by Machine.remove()), it will disconnect its UGens' audio connections. This methods makes it possible for UGen to be attached to either a different Shred or to no Shred if `null` is passed in. The latter makes this UGen a kind of \"free agent\" that is subject to the garbage collector as usual, but no longer to a origin Shred. This is useful ";
// if( !type_engine_import_mfun( env, func ) ) goto error;

// end
type_engine_import_class_end( env );

Expand Down
62 changes: 42 additions & 20 deletions src/core/chuck_ugen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,19 +236,29 @@ void Chuck_UGen::init()

//-----------------------------------------------------------------------------
// name: done()
// desc: ...
// desc: this function is called when a UGen is about to be deleted, typically
// from the Chuck_UGen's destructor
//-----------------------------------------------------------------------------
void Chuck_UGen::done()
{
// ref count gotta be zero if we get to this function
assert( this->m_ref_count == 0 );

// check if we have origin shred reference
if( this->origin_shred )
{
// unregister from origin shred
origin_shred->remove( this );
// reset/release origin shred reference
setOriginShred( NULL );
}

assert( this->m_ref_count == 0 );

// disconnect
// disconnect from UGen graph
this->disconnect( TRUE );
// flag
m_valid = FALSE;

// reclaim lists
fa_done( m_src_list, m_src_cap );
fa_done( m_dest_list, m_dest_cap );
fa_done( m_src_uana_list, m_src_uana_cap );
Expand All @@ -261,8 +271,8 @@ void Chuck_UGen::done()
// for each multichan reference | 1.5.2.0
for( t_CKUINT i = 0; i < m_multi_chan_size; i++ )
{
// TODO: disconnect?

// disconnect each channel | 1.5.4.2 (ge) part of #ugen-refs
m_multi_chan[i]->disconnect( TRUE );
// release
CK_SAFE_RELEASE( m_multi_chan[i] );
}
Expand All @@ -273,12 +283,13 @@ void Chuck_UGen::done()
// zero out
m_multi_chan_size = 0;

// SPENCER: is this okay??? (added 1.3.0.0)
// changed to release | 1.5.2.0 (ge)
// disconnect inlet and outlet (chugraphs) | 1.5.4.2 (ge) part of #ugen-refs
if( m_inlet ) m_inlet->disconnect( TRUE );
if( m_outlet ) m_outlet->disconnect( TRUE );
// release the reference held by Chuck_UGen (C++) | 1.5.2.0 (ge)
CK_SAFE_RELEASE( m_inlet );
CK_SAFE_RELEASE( m_outlet );

// clean up inlet/outlet | 1.5.2.0 (ge)
// clean up inlet/outlet as held by member variables (ChucK) | 1.5.2.0 (ge)
if( m_is_subgraph ) ck_subgraph_cleaup_inlet_outlet( this );

// clean up array (added 1.3.0.0)
Expand Down Expand Up @@ -517,8 +528,11 @@ t_CKBOOL Chuck_UGen::add( Chuck_UGen * src, t_CKBOOL isUpChuck )

// append
fa_push_back( m_src_list, m_src_cap, m_num_src, src );
// increment source count
m_num_src++;
src->add_ref();
// 1.5.4.2 (ge) removed as part of #ugen-refs
// src->add_ref();
// add from other side
src->add_by( this, isUpChuck );

// upchuck
Expand Down Expand Up @@ -588,7 +602,9 @@ void Chuck_UGen::add_by( Chuck_UGen * dest, t_CKBOOL isUpChuck )

// append
fa_push_back( m_dest_list, m_dest_cap, m_num_dest, dest );
dest->add_ref();
// 1.5.4.2 (ge) removed as part of #ugen-refs
// dest->add_ref();
// increment dest count
m_num_dest++;

// uana
Expand Down Expand Up @@ -659,7 +675,8 @@ t_CKBOOL Chuck_UGen::remove( Chuck_UGen * src )

m_src_list[--m_num_src] = NULL;
src->remove_by( this );
src->release();
// 1.5.4.2 (ge) removed as part of #ugen-refs
// src->release();
--k;
}
}
Expand Down Expand Up @@ -744,8 +761,8 @@ void Chuck_UGen::remove_by( Chuck_UGen * dest )
for( t_CKUINT j = i+1; j < m_num_dest; j++ )
m_dest_list[j-1] = m_dest_list[j];

// release
dest->release();
// 1.5.4.2 (ge) removed as part of #ugen-refs
// dest->release();
// null the last element
m_dest_list[--m_num_dest] = NULL;
i--;
Expand Down Expand Up @@ -796,7 +813,7 @@ void Chuck_UGen::remove_all( )

//-----------------------------------------------------------------------------
// name: disconnect()
// desc: ...
// desc: disconnect this UGen
//-----------------------------------------------------------------------------
t_CKBOOL Chuck_UGen::disconnect( t_CKBOOL recursive )
{
Expand All @@ -809,7 +826,7 @@ t_CKBOOL Chuck_UGen::disconnect( t_CKBOOL recursive )
return inlet()->disconnect( recursive );
}

// remove
// remove from dest
while( m_num_dest > 0 )
{
// make sure at least one got disconnected
Expand All @@ -828,9 +845,12 @@ t_CKBOOL Chuck_UGen::disconnect( t_CKBOOL recursive )
// m_dest_list[i]->remove( this );
// m_num_dest = 0;

// disconnect src too?
// recursive?
if( recursive )
{
// disconnect sources, too
this->remove_all();
}

return TRUE;
}
Expand Down Expand Up @@ -1379,7 +1399,8 @@ void Chuck_UGen::init_subgraph()
Chuck_Object * obj = NULL;

// instantiate object for inlet
obj = instantiate_and_initialize_object( this->origin_shred->vm_ref->env()->ckt_ugen, this->origin_shred );
// 1.5.4.2 (ge) remove special-case refs between UGen and shred; part of #ugen-refs
obj = instantiate_and_initialize_object( this->origin_vm->env()->ckt_ugen, this->origin_vm );
// set as inlet
m_inlet = (Chuck_UGen *)obj;
// additional reference count
Expand All @@ -1390,7 +1411,8 @@ void Chuck_UGen::init_subgraph()
// CK_SAFE_ADD_REF(this);

// instantiate object for outlet
obj = instantiate_and_initialize_object( this->origin_shred->vm_ref->env()->ckt_ugen, this->origin_shred );
// 1.5.4.2 (ge) remove special-case refs between UGen and shred; part of #ugen-refs
obj = instantiate_and_initialize_object( this->origin_vm->env()->ckt_ugen, this->origin_vm );
// set as outlet
m_outlet = (Chuck_UGen *)obj;
// additional reference count
Expand Down
36 changes: 4 additions & 32 deletions src/core/chuck_vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1902,28 +1902,12 @@ 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() )
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() )
{
// 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
Expand All @@ -1936,17 +1920,6 @@ void Chuck_VM_Shred::detach_ugens()
}
// 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();
}


Expand Down Expand Up @@ -2120,10 +2093,8 @@ t_CKBOOL Chuck_VM_Shred::add( Chuck_UGen * ugen )
return FALSE;

// increment reference count (added 1.3.0.0)
CK_SAFE_ADD_REF( ugen );

// RUBBISH
// cerr << "vm add ugen: 0x" << hex << (int)ugen << endl;
// remove ref-count from VM-side | 1.5.4.2 (ge) part of #ugen-refs
// CK_SAFE_ADD_REF( ugen );

m_ugen_map[ugen] = ugen;
return TRUE;
Expand All @@ -2145,7 +2116,8 @@ t_CKBOOL Chuck_VM_Shred::remove( Chuck_UGen * ugen )
m_ugen_map.erase( ugen );

// decrement reference count (added 1.3.0.0)
ugen->release();
// remove ref-count from VM-side | 1.5.4.2 (ge) part of #ugen-refs
// ugen->release();

return TRUE;
}
Expand Down
9 changes: 9 additions & 0 deletions src/core/ugen_xxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3042,6 +3042,15 @@ struct sndbuf_data

~sndbuf_data()
{
// open file descriptor? | 1.5.4.2 (ge) added #ugen-refs
if( this->fd )
{
// close file descriptor
sf_close( this->fd );
// zero out
this->fd = NULL;
}

CK_SAFE_DELETE_ARRAY( buffer );

if( chunk_map )
Expand Down
Loading