asCGarbageCollector::asSObjTypePair asCGarbageCollector::GetOldObjectAtIdx(int idx) { // We need to protect this access with a critical section as // another thread might be appending an object at the same time ENTERCRITICALSECTION(gcCritical); asSObjTypePair gcObj = gcOldObjects[idx]; LEAVECRITICALSECTION(gcCritical); return gcObj; }
void *asCMemoryMgr::AllocScriptNode() { ENTERCRITICALSECTION(cs); if( scriptNodePool.GetLength() ) { void *tRet = scriptNodePool.PopLast(); LEAVECRITICALSECTION(cs); return tRet; } LEAVECRITICALSECTION(cs); #if defined(AS_DEBUG) return ((asALLOCFUNCDEBUG_t)(userAlloc))(sizeof(asCScriptNode), __FILE__, __LINE__); #else return userAlloc(sizeof(asCScriptNode)); #endif }
objrtn ObjectPool_initialize(void) { static CRITICALSECTION cs; static int volatile once = 0; ENTERCRITICALSECTION(_CI_CS_); if (!once) { INITIALIZECRITICALSECTION(cs); once = 1; } LEAVECRITICALSECTION(_CI_CS_); ENTERCRITICALSECTION(cs); if (ObjectPool_c) { LEAVECRITICALSECTION(cs); return ObjectPool_c; } INHIBIT_THREADER; ObjectPool_c = gNewClass(Class, "ObjectPool", sizeof(ObjectPool_iv_t), sizeof(ObjectPool_cv_t), END); cMethodFor(ObjectPool, gNewGlobalPoolWithSize, ObjectPool_cm_gNewGlobalPoolWithSize); cMethodFor(ObjectPool, gDisposeGlobalPool, ObjectPool_cm_gDisposeGlobalPool); cMethodFor(ObjectPool, gRemoveFromPool, ObjectPool_cm_gRemoveFromPool); cMethodFor(ObjectPool, gNewWithInt, ObjectPool_cm_gNewWithInt); cMethodFor(ObjectPool, gAddToPool, ObjectPool_cm_gAddToPool); cMethodFor(ObjectPool, gNew, ObjectPool_cm_gNew); cMethodFor(ObjectPool, gNewGlobalPool, ObjectPool_cm_gNewGlobalPool); cMethodFor(ObjectPool, gDisposeObject, ObjectPool_cm_gDisposeObject); iMethodFor(ObjectPool, gRemoveFromPool, ObjectPool_im_gRemoveFromPool); iMethodFor(ObjectPool, gAddToPool, ObjectPool_im_gAddToPool); iMethodFor(ObjectPool, gDispose, ObjectPool_im_gDispose); iMethodFor(ObjectPool, gDeepDispose, ObjectPool_im_gDispose); iMethodFor(ObjectPool, gDisposeObject, ObjectPool_im_gDisposeObject); ObjectPool_cv = GetCVs(ObjectPool); ENABLE_THREADER; LEAVECRITICALSECTION(cs); return ObjectPool_c; }
void asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) { engine->CallObjectMethod(obj, objType->beh.addref); asSObjTypePair ot = {obj, objType}; // Add the data to the gcObjects array in a critical section as // another thread might be calling this method at the same time ENTERCRITICALSECTION(gcCritical); gcObjects.PushLast(ot); LEAVECRITICALSECTION(gcCritical); }
void asCGarbageCollector::RemoveOldObjectAtIdx(int idx) { // We need to protect this update with a critical section as // another thread might be appending an object at the same time ENTERCRITICALSECTION(gcCritical); if( idx == (int)gcOldObjects.GetLength() - 1) gcOldObjects.PopLast(); else gcOldObjects[idx] = gcOldObjects.PopLast(); LEAVECRITICALSECTION(gcCritical); }
void asCGarbageCollector::AddScriptObjectToGC(void *obj, asCObjectType *objType) { engine->CallObjectMethod(obj, objType->beh.addref); asSObjTypePair ot = {obj, objType, 0}; // Invoke the garbage collector to destroy a little garbage as new comes in // This will maintain the number of objects in the GC at a maintainable level without // halting the application, and without burdening the application with manually invoking the // garbage collector. if( engine->ep.autoGarbageCollect && gcNewObjects.GetLength() ) { // If the GC is already processing in another thread, then don't try this again // TODO: What if it is already processing in this thread? if( TRYENTERCRITICALSECTION(gcCollecting) ) { // TODO: The number of iterations should be dynamic, and increase // if the number of objects in the garbage collector grows high // Run one step of DetectGarbage if( gcOldObjects.GetLength() ) { IdentifyGarbageWithCyclicRefs(); DestroyOldGarbage(); } // Run a few steps of DestroyGarbage int iter = (int)gcNewObjects.GetLength(); if( iter > 10 ) iter = 10; while( iter-- > 0 ) DestroyNewGarbage(); LEAVECRITICALSECTION(gcCollecting); } } // Add the data to the gcObjects array in a critical section as // another thread might be calling this method at the same time ENTERCRITICALSECTION(gcCritical); gcNewObjects.PushLast(ot); LEAVECRITICALSECTION(gcCritical); }
void asCMemoryMgr::FreeScriptNode(void *ptr) { ENTERCRITICALSECTION(cs); // Pre allocate memory for the array to avoid slow growth if( scriptNodePool.GetLength() == 0 ) scriptNodePool.Allocate(100, 0); scriptNodePool.PushLast(ptr); LEAVECRITICALSECTION(cs); }
void *MA_realloc(void *arg, unsigned n) { char *m; Header *h = (Header *) arg; ENTERCRITICALSECTION(CS); if (!h || (--h)->status != 'U') { LEAVECRITICALSECTION(CS); return NULL; } if (h->size >= n) { h->rsize = n; LEAVECRITICALSECTION(CS); return h+1; } m = (char *) MA_malloc(n, h->next); memcpy(m, h+1, h->rsize); h->next = NULL; MA_free(h+1); LEAVECRITICALSECTION(CS); return m; }
void MA_free(void *arg) { int i; Header *h = (Header *) arg; ENTERCRITICALSECTION(CS); if (!h || (--h)->status != 'U') { LEAVECRITICALSECTION(CS); return; } for (i=1 ; Map[i].userBytes && Map[i].userBytes <= h->size ; ++i); i--; h->status = 'F'; if (h->next) *((char **) h->next) = NULL; h->next = Map[i].h; Map[i].h = h; LEAVECRITICALSECTION(CS); }
objrtn PointerArray_initialize(void) { static CRITICALSECTION cs; static int volatile once = 0; ENTERCRITICALSECTION(_CI_CS_); if (!once) { INITIALIZECRITICALSECTION(cs); once = 1; } LEAVECRITICALSECTION(_CI_CS_); ENTERCRITICALSECTION(cs); if (PointerArray_c) { LEAVECRITICALSECTION(cs); return PointerArray_c; } INHIBIT_THREADER; Array_initialize(); if (PointerArray_c) { ENABLE_THREADER; LEAVECRITICALSECTION(cs); return PointerArray_c; } PointerArray_c = gNewClass(Class, "PointerArray", 0, 0, Array, END); cvMethodFor(PointerArray, vNew, PointerArray_cvm_vNew, PointerArray_cfm_vNew); ivMethodFor(PointerArray, vChangeValue, PointerArray_ivm_vChangeValue, PointerArray_ifm_vChangeValue); ivMethodFor(PointerArray, vPointerValue, PointerArray_ivm_vPointerValue, PointerArray_ifm_vPointerValue); init_class(); ENABLE_THREADER; LEAVECRITICALSECTION(cs); return PointerArray_c; }
int asCThreadManager::CleanupLocalData() { if( threadManager == 0 ) return 0; #ifndef AS_NO_THREADS int r = 0; #if defined AS_POSIX_THREADS asPWORD id = (asPWORD)pthread_self(); #elif defined AS_WINDOWS_THREADS asPWORD id = (asPWORD)GetCurrentThreadId(); #endif ENTERCRITICALSECTION(threadManager->criticalSection); asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0; if( threadManager->tldMap.MoveTo(&cursor, id) ) { asCThreadLocalData *tld = threadManager->tldMap.GetValue(cursor); // Can we really remove it at this time? if( tld->activeContexts.GetLength() == 0 ) { asDELETE(tld,asCThreadLocalData); threadManager->tldMap.Erase(cursor); r = 0; } else r = asCONTEXT_ACTIVE; } LEAVECRITICALSECTION(threadManager->criticalSection); return r; #else if( threadManager->tld ) { if( threadManager->tld->activeContexts.GetLength() == 0 ) { asDELETE(threadManager->tld,asCThreadLocalData); threadManager->tld = 0; } else return asCONTEXT_ACTIVE; } return 0; #endif }
void asCMemoryMgr::FreeUnusedMemory() { // It's necessary to protect the scriptNodePool from multiple // simultaneous accesses, as the parser is used by several methods // that can be executed simultaneously. ENTERCRITICALSECTION(cs); int n; for( n = 0; n < (signed)scriptNodePool.GetLength(); n++ ) userFree(scriptNodePool[n]); scriptNodePool.Allocate(0, false); LEAVECRITICALSECTION(cs); // The engine already protects against multiple threads // compiling scripts simultaneously so this pool doesn't have // to be protected again. for( n = 0; n < (signed)byteInstructionPool.GetLength(); n++ ) userFree(byteInstructionPool[n]); byteInstructionPool.Allocate(0, false); }
void asCThreadManager::Prepare() { // The critical section cannot be declared globally, as there is no // guarantee for the order in which global variables are initialized // or uninitialized. // For this reason it's not possible to prevent two threads from calling // AddRef at the same time, so there is a chance for a race condition here. // To avoid the race condition when the thread manager is first created, // the application must make sure to call the global asPrepareForMultiThread() // in the main thread before any other thread creates a script engine. if( threadManager == 0 ) threadManager = asNEW(asCThreadManager); else { ENTERCRITICALSECTION(threadManager->criticalSection); threadManager->refCount++; LEAVECRITICALSECTION(threadManager->criticalSection); } }
int asCThreadManager::Prepare(asIThreadManager *externalThreadMgr) { // Don't allow an external thread manager if there // is already a thread manager defined if( externalThreadMgr && threadManager ) return asINVALID_ARG; // The critical section cannot be declared globally, as there is no // guarantee for the order in which global variables are initialized // or uninitialized. // For this reason it's not possible to prevent two threads from calling // AddRef at the same time, so there is a chance for a race condition here. // To avoid the race condition when the thread manager is first created, // the application must make sure to call the global asPrepareForMultiThread() // in the main thread before any other thread creates a script engine. if( threadManager == 0 && externalThreadMgr == 0 ) threadManager = asNEW(asCThreadManager); else { // If an application uses different dlls each dll will get it's own memory // space for global variables. If multiple dlls then uses AngelScript's // global thread support functions it is then best to share the thread // manager to make sure all dlls use the same critical section. if( externalThreadMgr ) threadManager = reinterpret_cast<asCThreadManager*>(externalThreadMgr); ENTERCRITICALSECTION(threadManager->criticalSection); threadManager->refCount++; LEAVECRITICALSECTION(threadManager->criticalSection); } // Success return 0; }
int asCGarbageCollector::GarbageCollect(asDWORD flags) { // If the GC is already processing in another thread, then don't enter here again if( TRYENTERCRITICALSECTION(gcCollecting) ) { // If the GC is already processing in this thread, then don't enter here again if( isProcessing ) { LEAVECRITICALSECTION(gcCollecting); return 1; } isProcessing = true; bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); if( flags & asGC_FULL_CYCLE ) { // Reset the state if( doDetect ) { // Move all objects to the old list, so we guarantee that all is detected for( asUINT n = (asUINT)gcNewObjects.GetLength(); n-- > 0; ) MoveObjectToOldList(n); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } unsigned int count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( IdentifyGarbageWithCyclicRefs() == 1 ) {} // Now destroy all known garbage if( doDestroy ) { while( DestroyNewGarbage() == 1 ) {} while( DestroyOldGarbage() == 1 ) {} } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()) ) count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); else { // Let the engine destroy the types that reached refCount 0 // If none were destroyed, then leave the GC // TODO: The asCObjectType should destroy its content when refCount reaches 0 // since no-one is using them. The registered types should have their // refcount increased by the config groups. Doing it like that will allow // me to remove this call to ClearUnusedTypes() that the GC really // shouldn't be calling. if( engine->ClearUnusedTypes() == 0 ) break; } } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); return 0; } else { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect ) IdentifyGarbageWithCyclicRefs(); } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }
void asCLockableSharedBool::Unlock() const { LEAVECRITICALSECTION(lock); }
int asCGarbageCollector::GarbageCollect(asDWORD flags) { // If the GC is already processing in another thread, then don't enter here again // TODO: What if it is already processing in this thread? if( TRYENTERCRITICALSECTION(gcCollecting) ) { bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); if( flags & asGC_FULL_CYCLE ) { // Reset the state if( doDetect ) { // Move all objects to the old list, so we guarantee that all is detected for( asUINT n = (asUINT)gcNewObjects.GetLength(); n-- > 0; ) MoveObjectToOldList(n); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } int r = 1; unsigned int count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( (r = IdentifyGarbageWithCyclicRefs()) == 1 ); // Now destroy all known garbage if( doDestroy ) { while( (r = DestroyNewGarbage()) == 1 ); while( (r = DestroyOldGarbage()) == 1 ); } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()) ) count = (unsigned int)(gcNewObjects.GetLength() + gcOldObjects.GetLength()); else break; } // Take the opportunity to clear unused types as well engine->ClearUnusedTypes(); LEAVECRITICALSECTION(gcCollecting); return 0; } else { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect ) IdentifyGarbageWithCyclicRefs(); } LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }
// TODO: Should have a flag to tell the garbage collector to automatically determine how many iterations are needed // It should then gather statistics such as how many objects has been created since last run, and how many objects // are destroyed per iteration, and how many objects are detected as cyclic garbage per iteration. // It should try to reach a stable number of objects, i.e. so that on average the number of objects added to // the garbage collector is the same as the number of objects destroyed. And it should try to minimize the number // of iterations of detections that must be executed per cycle while still identifying the cyclic garbage // These variables should also be available for inspection through the gcstatistics. int asCGarbageCollector::GarbageCollect(asDWORD flags, asUINT iterations) { // If the GC is already processing in another thread, then don't enter here again if( TRYENTERCRITICALSECTION(gcCollecting) ) { // If the GC is already processing in this thread, then don't enter here again if( isProcessing ) { LEAVECRITICALSECTION(gcCollecting); return 1; } isProcessing = true; bool doDetect = (flags & asGC_DETECT_GARBAGE) || !(flags & asGC_DESTROY_GARBAGE); bool doDestroy = (flags & asGC_DESTROY_GARBAGE) || !(flags & asGC_DETECT_GARBAGE); if( flags & asGC_FULL_CYCLE ) { // Reset the state if( doDetect ) { // Move all new objects to the old list, so we guarantee that all is detected MoveAllObjectsToOldList(); detectState = clearCounters_init; } if( doDestroy ) { destroyNewState = destroyGarbage_init; destroyOldState = destroyGarbage_init; } // The full cycle only works with the objects in the old list so that the // set of objects scanned for garbage is fixed even if new objects are added // by other threads in parallel. unsigned int count = (unsigned int)(gcOldObjects.GetLength()); for(;;) { // Detect all garbage with cyclic references if( doDetect ) while( IdentifyGarbageWithCyclicRefs() == 1 ) {} // Now destroy all known garbage if( doDestroy ) { if( !doDetect ) while( DestroyNewGarbage() == 1 ) {} while( DestroyOldGarbage() == 1 ) {} } // Run another iteration if any garbage was destroyed if( count != (unsigned int)(gcOldObjects.GetLength()) ) count = (unsigned int)(gcOldObjects.GetLength()); else break; } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); return 0; } else { while( iterations-- > 0 ) { // Destroy the garbage that we know of if( doDestroy ) { DestroyNewGarbage(); DestroyOldGarbage(); } // Run another incremental step of the identification of cyclic references if( doDetect && gcOldObjects.GetLength() > 0 ) IdentifyGarbageWithCyclicRefs(); } } isProcessing = false; LEAVECRITICALSECTION(gcCollecting); } // Return 1 to indicate that the cycle wasn't finished return 1; }
void *MA_malloc(unsigned n, void *p) { unsigned sz; int i; Header *h; Header *ph; Header *bh; Header *pbh; unsigned tnb; unsigned nbn; unsigned nbl; if (!CS_init) { INITIALIZECRITICALSECTION(CS); CS_init = 1; } ENTERCRITICALSECTION(CS); if (!n) ++n; sz = RND(n); for (i=1 ; Map[i].userBytes && Map[i].userBytes <= sz ; ++i); i--; for (bh=pbh=ph=NULL, h=Map[i].h ; h ; ph=h, h=h->next) if (h->size == sz) { if (ph) ph->next = h->next; else Map[i].h = h->next; h->status = 'U'; h->next = (Header *) p; h->rsize = n; LEAVECRITICALSECTION(CS); return h+1; } else if (h->size > sz && (!bh || bh->size > h->size)) { pbh = ph; bh = h; } if (bh) { if (pbh) pbh->next = bh->next; else Map[i].h = bh->next; bh->status = 'U'; bh->next = (Header *) p; bh->rsize = n; LEAVECRITICALSECTION(CS); return bh+1; } for (i++ ; Map[i].userBytes && !Map[i].h ; ++i); if (!Map[i].userBytes) { more_core(n); LEAVECRITICALSECTION(CS); return MA_malloc(n, p); } #line 169 "memalloc.d" h = Map[i].h; Map[i].h = h->next; tnb = h->size / sizeof(Header); nbn = sz / sizeof(Header); nbl = tnb - nbn; if (nbl > 5) { bh = h + (nbn + 1); bh->status = 'U'; bh->next = NULL; bh->rsize = bh->size = (nbl - 1) * sizeof(Header); h->size -= nbl * sizeof(Header); MA_free(bh+1); } h->status = 'U'; h->next = (Header *) p; h->rsize = n; LEAVECRITICALSECTION(CS); return h+1; }
BOOL bGetANSISetMap() { CHAR ch[256]; ULONG *pul; ULONG ii,jj; NTSTATUS st; ULONG cjResult; WCHAR *pwc; WCHAR *pwcSBC; // See if we know the answers already. (The client should not have called!) if (gpwcANSICharSet != (WCHAR *) NULL) return(TRUE); // Create a mapping. // Make an ANSI source char set. This funny way of initialization takes // about 180 instructions to execute rather than over 1000. pul = (ULONG *) ch; for (ii=0x03020100L,jj=0; jj<64; jj+=4) { pul[jj+0] = ii; ii += 0x04040404L; pul[jj+1] = ii; ii += 0x04040404L; pul[jj+2] = ii; ii += 0x04040404L; pul[jj+3] = ii; ii += 0x04040404L; } // Allocate the UNICODE buffer but don't write the pointer in until the // table is valid, in case we're racing another thread. pwc = LOCALALLOC(512 * sizeof(WCHAR)); pwcSBC = &pwc[256]; if (pwc == (WCHAR *) NULL) return(FALSE); // Convert the characters. pwc[0] = 0; st = RtlMultiByteToUnicodeN ( &pwc[1], // OUT PWCH UnicodeString 255 * sizeof(WCHAR), // IN ULONG MaxBytesInUnicodeString &cjResult, // OUT PULONG BytesInUnicodeString &ch[1], // IN PCH MultiByteString 255 // IN ULONG BytesInMultiByteString ); if( !NT_SUCCESS(st) ) { // Clean up and forget about accelerations. WARNING("GDI32: RtlMultiByteToUnicodeN error."); LOCALFREE(pwc); return(FALSE); } if( cjResult != 255 * sizeof(WCHAR) ) { // There must be a DBCS code page so gpwcANSIMap takes on new meaning. // It is used for fonts with ANSI,OEM, and SYMBOL charsets. Also, // another table, gpwcDBCS is constructed that is used to map the SBCS // of SHIFT-JIS fonts. WARNING("GDI32:Assuming DBCS code page.\n"); st = MultiByteToWideChar ( 1252, // code page to use 0, // flags &ch[1], // characters to translate 255, // number of multibyte characters &pwc[1], // unicode values of characters 255 // number of wide characters ); if( !NT_SUCCESS(st) ) { // Clean up and forget about accelerations. WARNING("GDI32: MultiByteToWideChar error."); LOCALFREE(pwc); return(FALSE); } // Okay now make a table for SBC bytes. Mark DBCS lead bytes // with 0xFFFF. for( jj = 0; jj < 256; jj++ ) { if( IsDBCSLeadByte( (UCHAR)jj )) { pwcSBC[jj] = (WCHAR) 0xFFFF; } else { st = RtlMultiByteToUnicodeN ( &pwcSBC[jj], sizeof(WCHAR), &cjResult, &ch[jj], 1 ); if( !NT_SUCCESS(st) ) { WARNING("GDI32: RtlMultByteToUnicodeN error."); LOCALFREE(pwc); return(FALSE); } } } } // The table is good, jam it in. Watch out for another thread running this // routine simultaneously. ENTERCRITICALSECTION(&semLocal); { if (gpwcANSICharSet == (WCHAR *) NULL) { gpwcANSICharSet = pwc; gpwcDBCSCharSet = pwcSBC; pwc = (WCHAR *) NULL; } } LEAVECRITICALSECTION(&semLocal); // If we collided with another thread, clean up our extra space. if (pwc != (WCHAR *) NULL) LOCALFREE(pwc); // At this point we have a valid mapping. return(TRUE); }