From 20349de826641fd1d163152c14979402136230b1 Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Mon, 4 Dec 2023 23:11:03 -0800 Subject: [PATCH] add auto-release of object mvars; updates to refcount to chugraph internal elements and others --- src/core/chuck_instr.cpp | 6 +- src/core/chuck_lang.cpp | 42 ++++------- src/core/chuck_oo.cpp | 19 ++++- src/core/chuck_type.cpp | 6 ++ src/core/chuck_type.h | 2 + src/core/chuck_ugen.cpp | 79 ++++++++++++-------- src/core/chuck_ugen.h | 2 +- src/core/ugen_xxx.cpp | 67 ++++++++++++----- src/core/ugen_xxx.h | 3 + src/test/01-Basic/245-depend-class-extend.ck | 5 +- src/test/01-Basic/249-dtor-cascade.ck | 22 ++++++ 11 files changed, 174 insertions(+), 79 deletions(-) create mode 100644 src/test/01-Basic/249-dtor-cascade.ck diff --git a/src/core/chuck_instr.cpp b/src/core/chuck_instr.cpp index d57d93621..94249078f 100644 --- a/src/core/chuck_instr.cpp +++ b/src/core/chuck_instr.cpp @@ -4341,12 +4341,12 @@ t_CKBOOL initialize_object( Chuck_Object * object, Chuck_Type * type, Chuck_VM_S // cast to ugen ugen->m_multi_chan[i] = (Chuck_UGen *)obj; // additional reference count - ugen->m_multi_chan[i]->add_ref(); + CK_SAFE_ADD_REF(obj); // owner - ugen->m_multi_chan[i]->owner = ugen; + ugen->m_multi_chan[i]->owner_ugen = ugen; // ref count // spencer 2013-5-20: don't add extra ref, to avoid a ref cycle - //ugen->add_ref(); + // ugen->add_ref(); } // TODO: alloc channels for uana } diff --git a/src/core/chuck_lang.cpp b/src/core/chuck_lang.cpp index 19f720c35..b831f1cc7 100644 --- a/src/core/chuck_lang.cpp +++ b/src/core/chuck_lang.cpp @@ -246,7 +246,7 @@ t_CKBOOL init_class_uana( Chuck_Env * env, Chuck_Type * type ) return FALSE; // add variables - uana_offset_blob = type_engine_import_mvar( env, "UAnaBlob", "m_blob", FALSE ); + uana_offset_blob = type_engine_import_mvar( env, "int", "@m_blobproxy", FALSE ); if( uana_offset_blob == CK_INVALID_OFFSET ) goto error; // add upchuck @@ -1551,10 +1551,8 @@ CK_DLL_DTOR( object_dtor ) // log EM_log( CK_LOG_ALL, "Object destructor..." ); - // get the string - Chuck_String * str = (Chuck_String *)OBJ_MEMBER_UINT(SELF, Object_offset_string); - // release - CK_SAFE_RELEASE( str ); + // NOTE the garbage collector should take care of + // string reference at offset Object_offset_string } @@ -2032,34 +2030,26 @@ CK_DLL_CTOR( uanablob_ctor ) OBJ_MEMBER_TIME(SELF, uanablob_offset_when) = 0; // fvals - Chuck_ArrayFloat * arr8 = new Chuck_ArrayFloat( 8 ); - initialize_object( arr8, SHRED->vm_ref->env()->ckt_array, SHRED, VM ); - arr8->add_ref(); - OBJ_MEMBER_INT(SELF, uanablob_offset_fvals) = (t_CKINT)arr8; + Chuck_ArrayFloat * arrF = new Chuck_ArrayFloat( 8 ); + initialize_object( arrF, SHRED->vm_ref->env()->ckt_array, SHRED, VM ); + CK_SAFE_ADD_REF( arrF ); + OBJ_MEMBER_INT(SELF, uanablob_offset_fvals) = (t_CKINT)arrF; - // cvals - Chuck_ArrayVec2 * arr16 = new Chuck_ArrayVec2( 8 ); - initialize_object( arr16, SHRED->vm_ref->env()->ckt_array, SHRED, VM ); - arr16->add_ref(); - OBJ_MEMBER_INT(SELF, uanablob_offset_cvals) = (t_CKINT)arr16; + // cvals (complex) + Chuck_ArrayVec2 * arrC = new Chuck_ArrayVec2( 8 ); + initialize_object( arrC, SHRED->vm_ref->env()->ckt_array, SHRED, VM ); + CK_SAFE_ADD_REF( arrC ); + OBJ_MEMBER_INT(SELF, uanablob_offset_cvals) = (t_CKINT)arrC; } // dtor CK_DLL_DTOR( uanablob_dtor ) { - // get array - Chuck_ArrayFloat * arr8 = (Chuck_ArrayFloat *)OBJ_MEMBER_INT(SELF, uanablob_offset_fvals); - // release it - arr8->release(); - OBJ_MEMBER_INT(SELF, uanablob_offset_fvals) = 0; - - // get array - Chuck_ArrayVec2 * arr16 = (Chuck_ArrayVec2 *)OBJ_MEMBER_INT(SELF, uanablob_offset_cvals); - // release it - arr16->release(); - OBJ_MEMBER_INT(SELF, uanablob_offset_cvals) = 0; - + // set when to 0 for good measure OBJ_MEMBER_TIME(SELF, uanablob_offset_when) = 0; + + // typed object mvars are auto-released | 1.5.2.0 + // include uanablob_offset_fvals and uanablob_offset_cvals } CK_DLL_MFUN( uanablob_when ) diff --git a/src/core/chuck_oo.cpp b/src/core/chuck_oo.cpp index 866baa665..b41673d63 100644 --- a/src/core/chuck_oo.cpp +++ b/src/core/chuck_oo.cpp @@ -330,7 +330,24 @@ Chuck_Object::~Chuck_Object() type = type->parent; } - // TODO: release class-scope member vars + // release class-scope member vars | 1.5.2.0 (ge) added + Chuck_Type * t = type_ref; + Chuck_Object * obj = NULL; + // for each type in the inheritance chain + while( t ) + { + // for each mvar directly in the class + for( t_CKUINT i = 0; i < t->obj_mvars_offsets.size(); i++ ) + { + // get the object reference from the offsets + obj = OBJ_MEMBER_OBJECT( this, t->obj_mvars_offsets[i] ); + // release + CK_SAFE_RELEASE(obj); + } + + // go up to parent type + t = t->parent; + } // release origin shred CK_SAFE_RELEASE( origin_shred ); diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index c6e101bd3..5663b0f7c 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -4181,6 +4181,12 @@ t_CKTYPE type_engine_check_exp_decl_part2( Chuck_Env * env, a_Exp_Decl decl ) { // offset value->offset = env->curr->offset; + // if at class_scope + if( env->class_def && env->class_scope == 0 && isobj(env,type) ) + { + // add it to the class | 1.5.2.0 (ge) + env->class_def->obj_mvars_offsets.push_back( value->offset ); + } /******************************************************************* * spencer: added this into function to provide the same logic path diff --git a/src/core/chuck_type.h b/src/core/chuck_type.h index e5afa5551..458daa1a7 100644 --- a/src/core/chuck_type.h +++ b/src/core/chuck_type.h @@ -944,6 +944,8 @@ struct Chuck_Type : public Chuck_Object f_alloc allocator; // origin hint ckte_Origin originHint; + // offsets of mvars that are Objects + std::vector obj_mvars_offsets; // (within-context, e.g., a ck file) dependency tracking | 1.5.0.8 Chuck_Value_Dependency_Graph depends; diff --git a/src/core/chuck_ugen.cpp b/src/core/chuck_ugen.cpp index 1bd539c22..2eb081bcd 100644 --- a/src/core/chuck_ugen.cpp +++ b/src/core/chuck_ugen.cpp @@ -39,6 +39,7 @@ #include "chuck_vm.h" #include "chuck_lang.h" #include "chuck_errmsg.h" +#include "ugen_xxx.h" // for subgraph ops using namespace std; @@ -212,7 +213,7 @@ void Chuck_UGen::init() m_sum_v = NULL; m_current_v = NULL; - owner = NULL; + owner_ugen = NULL; // what a hack m_is_uana = FALSE; @@ -256,13 +257,28 @@ void Chuck_UGen::done() CK_SAFE_DELETE_ARRAY( m_sum_v ); CK_SAFE_DELETE_ARRAY( m_current_v ); + // for each multichan reference | 1.5.2.0 + for( t_CKUINT i = 0; i < m_multi_chan_size; i++ ) + { + // TODO: disconnect? + + // release + CK_SAFE_RELEASE( m_multi_chan[i] ); + } + // more reclaim (added 1.3.0.0) - CK_SAFE_DELETE_ARRAY( m_multi_chan ); // TODO: m_multi_chan, break ref count loop + CK_SAFE_DELETE_ARRAY( m_multi_chan ); + // zero out + m_multi_chan_size = 0; + + // SPENCER: is this okay??? (added 1.3.0.0) + // changed to release | 1.5.2.0 (ge) + CK_SAFE_RELEASE( m_inlet ); + CK_SAFE_RELEASE( m_outlet ); - // SPENCERTODO: is this okay??? (added 1.3.0.0) - CK_SAFE_DELETE( m_inlet ); - CK_SAFE_DELETE( m_outlet ); + // clean up inlet/outlet | 1.5.2.0 (ge) + if( m_is_subgraph ) ck_subgraph_cleaup_inlet_outlet( this ); // clean up array (added 1.3.0.0) CK_SAFE_DELETE_ARRAY( m_multi_in_v ); @@ -308,19 +324,21 @@ void Chuck_UGen::alloc_multi_chan( t_CKUINT num_ins, t_CKUINT num_outs ) { // get max of num_ins and num_outs m_multi_chan_size = ( num_ins > num_outs ? num_ins : num_outs ); - - // allocate - m_multi_chan = new Chuck_UGen *[m_multi_chan_size]; - // zero it out, whoever call this will fill in - memset( m_multi_chan, 0, m_multi_chan_size * sizeof(Chuck_UGen *) ); + // assert null (no auto-cleanup of any existing m_multi_chan array) + assert( m_multi_chan == NULL ); // mono if( m_multi_chan_size == 1 ) { // zero out m_multi_chan_size = 0; - // self - m_multi_chan[0] = this; + } + else // not mono + { + // allocate + m_multi_chan = new Chuck_UGen *[m_multi_chan_size]; + // zero it out, whoever call this will fill in + memset( m_multi_chan, 0, m_multi_chan_size * sizeof(Chuck_UGen *) ); } // if there tick-frame (i.e., has multi-channel tick function; added 1.3.0.0) @@ -433,7 +451,7 @@ void Chuck_UGen::get_buffer( SAMPLE * buffer, t_CKINT num_elem ) //----------------------------------------------------------------------------- Chuck_UGen * Chuck_UGen::src_chan( t_CKUINT chan ) { - if( this->m_num_outs == 1) + if( this->m_num_outs == 1 ) return this; return m_multi_chan[chan%m_num_outs]; } @@ -448,7 +466,7 @@ Chuck_UGen * Chuck_UGen::src_chan( t_CKUINT chan ) //----------------------------------------------------------------------------- Chuck_UGen * Chuck_UGen::dst_for_src_chan( t_CKUINT chan ) { - if( this->m_num_ins == 1) + if( this->m_num_ins == 1 ) return this; if( chan < this->m_num_ins ) return m_multi_chan[chan]; @@ -955,13 +973,13 @@ t_CKBOOL Chuck_UGen::system_tick( t_CKTIME now ) } // if owner (i.e., this ugen is one of the channels in a multi-channel ugen) - if( owner != NULL && owner->m_time < now ) + if( owner_ugen != NULL && owner_ugen->m_time < now ) { // tick the owner - owner->system_tick( now ); + owner_ugen->system_tick( now ); // if the owner has a multichannel tick function (added 1.3.0.0) - if( owner->tickf ) + if( owner_ugen->tickf ) { // set the latest to the current m_last = m_current; @@ -1166,12 +1184,12 @@ t_CKBOOL Chuck_UGen::system_tick_v( t_CKTIME now, t_CKUINT numFrames ) } // if owner - if( owner != NULL && owner->m_time < now ) + if( owner_ugen != NULL && owner_ugen->m_time < now ) { - owner->system_tick_v( now, numFrames ); + owner_ugen->system_tick_v( now, numFrames ); // if the owner has a multichannel tick function (added 1.3.0.0) - if( owner->tickf ) + if( owner_ugen->tickf ) { // set the latest to the current m_last = m_current_v[numFrames - 1]; @@ -1333,22 +1351,22 @@ void Chuck_UGen::init_subgraph() // set as inlet m_inlet = (Chuck_UGen *)obj; // additional reference count - m_inlet->add_ref(); + CK_SAFE_ADD_REF(m_inlet); // owner - m_inlet->owner = this; - // ref count - this->add_ref(); + m_inlet->owner_ugen = this; + // no ref count | to avoid ref cycle | 1.5.2.0 + // 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 ); // set as outlet m_outlet = (Chuck_UGen *)obj; // additional reference count - m_outlet->add_ref(); + CK_SAFE_ADD_REF(m_outlet); // owner - m_outlet->owner = this; - // ref count - this->add_ref(); + m_outlet->owner_ugen = this; + // no ref count | to avoid ref cycle | 1.5.2.0 + // CK_SAFE_ADD_REF(this); } @@ -1542,10 +1560,11 @@ t_CKBOOL Chuck_UAna::system_tock( t_CKTIME now ) } // if owner - if( owner != NULL && owner->m_is_uana ) + if( owner_ugen != NULL && owner_ugen->m_is_uana ) { // cast to uana - uana = (Chuck_UAna *)owner; + // (requires RTT) dynamic_cast(owner_ugen); + uana = (Chuck_UAna *)owner_ugen; // tock it if( uana->m_uana_time < now ) uana->system_tock( now ); } diff --git a/src/core/chuck_ugen.h b/src/core/chuck_ugen.h index e4760f09a..1f39eb9a8 100644 --- a/src/core/chuck_ugen.h +++ b/src/core/chuck_ugen.h @@ -149,7 +149,7 @@ struct Chuck_UGen : public Chuck_Object SAMPLE * m_current_v; // owner - Chuck_UGen * owner; + Chuck_UGen * owner_ugen; // what a hack! t_CKBOOL m_is_uana; diff --git a/src/core/ugen_xxx.cpp b/src/core/ugen_xxx.cpp index fec6747b1..871a605c3 100644 --- a/src/core/ugen_xxx.cpp +++ b/src/core/ugen_xxx.cpp @@ -1536,27 +1536,61 @@ DLL_QUERY lisa_query( Chuck_DL_Query * QUERY ) //----------------------------------------------------------------------------- // name: subgraph_ctor() -// desc: ... +// desc: subgraph constructor //----------------------------------------------------------------------------- CK_DLL_CTOR( subgraph_ctor ) { // get ugen Chuck_UGen * ugen = (Chuck_UGen *)SELF; + // initialize the ugen as subgraph ugen->init_subgraph(); + // handle the inlet if( ugen->inlet() ) { - OBJ_MEMBER_OBJECT(SELF, subgraph_offset_inlet) = ugen->inlet(); + // get the ugen + Chuck_UGen * in = ugen->inlet(); + // set the mvar + OBJ_MEMBER_OBJECT(SELF, subgraph_offset_inlet) = in; + // add refcount for the mvar | 1.5.2.0 (ge) + // NOTE will be released as part of Object cleanup + CK_SAFE_ADD_REF(in); } + // handle the outlet if( ugen->outlet() ) { - OBJ_MEMBER_OBJECT(SELF, subgraph_offset_outlet) = ugen->outlet(); + // get the ugen + Chuck_UGen * out = ugen->outlet(); + // set the mvar + OBJ_MEMBER_OBJECT(SELF, subgraph_offset_outlet) = out; + // add refcount for the mvar | 1.5.2.0 (ge) + // NOTE will be released as part of Object cleanup + CK_SAFE_ADD_REF(out); } } +//----------------------------------------------------------------------------- +// name: ck_subgraph_cleaup_inlet_outlet() +// desc: release and cleanup subgraph inlet/outlet +//----------------------------------------------------------------------------- +void ck_subgraph_cleaup_inlet_outlet( Chuck_UGen * ugen ) +{ + Chuck_UGen * inlet = (Chuck_UGen *)OBJ_MEMBER_OBJECT(ugen, subgraph_offset_inlet); + Chuck_UGen * outlet = (Chuck_UGen *)OBJ_MEMBER_OBJECT(ugen, subgraph_offset_outlet); + + // release + CK_SAFE_RELEASE( inlet ); + CK_SAFE_RELEASE( outlet ); + + // zero out + OBJ_MEMBER_OBJECT(ugen, subgraph_offset_inlet) = NULL; + OBJ_MEMBER_OBJECT(ugen, subgraph_offset_outlet) = NULL; +} + + //----------------------------------------------------------------------------- // name: FooGen_Data // desc: ... @@ -1774,20 +1808,19 @@ CK_DLL_CTOR( stereo_ctor ) // default panning law to preserve unity gain (but a WTF panning scheme) OBJ_MEMBER_INT(SELF, stereo_offset_panType) = PAN_WTF_UNITY; - // multi - if( ugen->m_multi_chan_size ) - { - // set left - OBJ_MEMBER_UINT(SELF, stereo_offset_left) = (t_CKUINT)(ugen->m_multi_chan[0]); - // set right - OBJ_MEMBER_UINT(SELF, stereo_offset_right) = (t_CKUINT)(ugen->m_multi_chan[1]); - } - else // mono - { - // set left and right to self - OBJ_MEMBER_UINT(SELF, stereo_offset_left) = (t_CKUINT)ugen; - OBJ_MEMBER_UINT(SELF, stereo_offset_right) = (t_CKUINT)ugen; - } + // verify | 1.5.2.0 (ge) added + assert( ugen->m_multi_chan_size == 2 ); + + // set left + OBJ_MEMBER_UINT(SELF, stereo_offset_left) = (t_CKUINT)(ugen->m_multi_chan[0]); + // add ref | 1.5.2.0 + CK_SAFE_ADD_REF( ugen->m_multi_chan[0] ); + + // set right + OBJ_MEMBER_UINT(SELF, stereo_offset_right) = (t_CKUINT)(ugen->m_multi_chan[1]); + // add ref | 1.5.2.0 + CK_SAFE_ADD_REF( ugen->m_multi_chan[1] ); + } diff --git a/src/core/ugen_xxx.h b/src/core/ugen_xxx.h index 9dde93835..3063bb7f2 100644 --- a/src/core/ugen_xxx.h +++ b/src/core/ugen_xxx.h @@ -59,6 +59,9 @@ CK_DLL_CGET( multi_cget_chan ); // bunghole CK_DLL_TICK( bunghole_tick ); +// subgraph; release and cleanup subgraph inlet/outlet +void ck_subgraph_cleaup_inlet_outlet( Chuck_UGen * ugen ); + // pan2 CK_DLL_CTOR( pan2_ctor ); CK_DLL_DTOR( pan2_dtor ); diff --git a/src/test/01-Basic/245-depend-class-extend.ck b/src/test/01-Basic/245-depend-class-extend.ck index 07bf9e081..f62379300 100644 --- a/src/test/01-Basic/245-depend-class-extend.ck +++ b/src/test/01-Basic/245-depend-class-extend.ck @@ -1,6 +1,9 @@ // test declaring/instantation classes with inheritance, involving // a member variable, declared in the file below its access -// was issue #376 +// +// related: +// https://github.com/ccrma/chuck/issues/349 +// https://github.com/ccrma/chuck/issues/376 public class Foo { diff --git a/src/test/01-Basic/249-dtor-cascade.ck b/src/test/01-Basic/249-dtor-cascade.ck new file mode 100644 index 000000000..33df14315 --- /dev/null +++ b/src/test/01-Basic/249-dtor-cascade.ck @@ -0,0 +1,22 @@ +// verify cascading destructors; in order for this to happen +// Objects need to release member variables upon clean-up + +class Foo +{ + fun @destruct() + { + // if it prints that means destructor invoked + <<< "success" >>>; + } +} + +class Bar +{ + // an object + Foo foo; +} + +// make a Bar +Bar bar; + +// when bar goes out of scope, its content should be released as well