Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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);
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 7
0
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);
}
Ejemplo n.º 8
0
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; 
} 
Ejemplo n.º 9
0
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); 
} 
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
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
}
Ejemplo n.º 12
0
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);
}
Ejemplo n.º 13
0
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);
	}
}
Ejemplo n.º 14
0
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;
}
Ejemplo n.º 15
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);
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
// 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;
}
Ejemplo n.º 19
0
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; 
} 
Ejemplo n.º 20
0
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);
}