Advertisement

Improved thread local storage for angelscript

Started by June 12, 2007 08:54 PM
11 comments, last by penguinpusher 17 years, 5 months ago
Just a suggestion leading on from the discussion here. Use the OS's thread local functionality, mostly it's no critical sections or mutexes required. They are also fairly easy to wrap, here is my own code for it, feel free to incorporate this into angelscript. Autocleanup of the TLS data on windows does require a dll, I simply split thread local storage into it's own lib on windows for that reason. This code makes the assumption that thread local keys are created in the main thread. It comes from my sourceforge project http://ldk.sourceforge.net/ and is zlib licensed, and there's other stuff in there that may well be useful for angelscript because I designed this library to become the base of a hard realtime scripting language.

//ThreadLocalStorage.h
extern "C"
{
//////////////////////////////////////////////////////////////////////////////////////
/// \ingroup Threading
/// \addtogroup ThreadLocalStorage
/// @{
///
/// Programs often need global or static variables that have different values in
/// different threads. Since threads share one memory space, this cannot be achieved
/// with regular variables. Thread local storage is the answer to this need.
///
/// Each thread possesses a private memory block, the thread-local data area (TLD).
/// This area is indexed by TLS keys. The TLD area associates values of type void *
/// to TLS keys. TLS keys are common to all threads, but the value associated with
/// a given TLS key can be different in each thread.
///
/// For concreteness, the TLS areas can be viewed as arrays of void * pointers, TLS
/// keys as integer indices into these arrays, and the value of a TLS key as the value
/// of the corresponding array element in the calling thread.
///
/// When a thread is created, its TLS area initially associates NULL with all keys.
/// This behavior is undocumented on MS Windows.
///
/// This description was adapted from the \em pthread_key_create GNU-Linux manpage.
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
/// \typedef void (*ThreadLocalDtor) (void*)
/// \brief A pointer to a function used to free a piece of thread local storage.
///////////////////////////////////////////////////////////////////////////////////////
typedef void (*ThreadLocalDtor) (void*);

//////////////////////////////////////////////////////////////////////////////////////
/// \typedef size_t TLSKey
/// \brief A key used to access thread local storage.
//////////////////////////////////////////////////////////////////////////////////////
typedef size_t TLSKey;

//////////////////////////////////////////////////////////////////////////////////////
/// \fn TLSKey threadLocalCreateKey(ThreadLocalDtor dtor=NULL)
/// \brief Create a key for thread local storage.
/// \param dtor A pointer to a function to free a piece of thread local storage.
/// \return A unique key to be used with \em threadLocalSet() and
/// \em threadLocalGet().
//////////////////////////////////////////////////////////////////////////////////////
LDK_TLS_API TLSKey threadLocalCreateKey(ThreadLocalDtor dtor=NULL);

//////////////////////////////////////////////////////////////////////////////////////
/// \fn void threadLocalSet(TLSKey key, void* object)
/// \brief Associate an object with a key for the current thread.
/// \param key The unique identifier associated with an object in the current thread.
/// \param object The object to associate with \em key in the current thread.
//////////////////////////////////////////////////////////////////////////////////////
LDK_TLS_API void threadLocalSet(TLSKey key, void* object);

//////////////////////////////////////////////////////////////////////////////////////
/// \fn void*  threadLocalGet(TLSKey key)
/// \brief Get the object associated with \em key for the current thread.
/// \param key The unique identifier associated with an object in the current thread.
/// \return NULL if there is nothing associated with a key in the current thread,
/// otherwise the associated object.
/////////////////////////////////////////////////////////////////////////////////////
LDK_TLS_API void* threadLocalGet(TLSKey key);

/// @}

// ThreadLocalStorage.cpp
#include "LDK/ThreadLocalStorage.h"
#include <assert.h>
#ifdef unix
#include <pthread.h>


void* threadLocalGet(TLSKey key)
{
    return pthread_getspecific(key);
}

TLSKey threadLocalCreateKey(ThreadLocalDtor dtor)
{
    TLSKey key = 0;
    int test = pthread_key_create((pthread_key_t*)&key, dtor);
    assert(!test);
    return key;
}

void threadLocalSet(TLSKey key, void* data)
{
    int test = pthread_setspecific(key, data);
    assert(!test);
}
#elif defined _WINDOWS

#include <windows.h>
#include <map>

#ifdef _MSC_VER
#pragma warning (disable : 4267)
#endif

namespace
{
typedef std::map<TLSKey,ThreadLocalDtor> TLSMap;
TLSMap gTLSCleanup;
}

void* threadLocalGet(TLSKey key)
{
    return TlsGetValue(key);
}

TLSKey threadLocalCreateKey(ThreadLocalDtor dtor)
{
    TLSKey key = TlsAlloc();
    assert(key != TLS_OUT_OF_INDEXES);
    gTLSCleanup[key] = dtor;
    return key;
}

void threadLocalSet(TLSKey key, void* data)
{
    int test = TlsSetValue(key, data);
    assert(test);
}

void threadLocalCleanup()
{
    TLSMap::iterator i = gTLSCleanup.begin();
    while(i != gTLSCleanup.end())
    {
        void* data = threadLocalGet((*i).first);
        if(data && (*i).second)
            (*i).second(data);
        i++;
    }
}

bool __stdcall DllMain(HINSTANCE hndl, DWORD reason, LPVOID reserved)
{
    switch(reason)
    {
        case DLL_PROCESS_ATTACH:
            //do nothing
            break;
        case DLL_THREAD_ATTACH:
            //do nothing
            break;
        case DLL_THREAD_DETACH:
            threadLocalCleanup();
            break;
        case DLL_PROCESS_DETACH:
            //do nothing?
            break;
    }
    return true;
}
#ifdef _MSC_VER
#pragma warning (default : 4267)
#endif

#endif //unix






[Edited by - penguinpusher on June 13, 2007 5:50:54 AM]
Lorien Dunn
Thanks, this looks interesting indeed. I'll see how I can adapt it to AngelScript. Though it seems the critical sections really aren't the bottle-neck for multithreaded applications.

And also, this only solves the thread local storage, critical sections will still be necessary for protected reference counters and so on.

Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Yep, I read that, but getting rid of crit sections won't do any harm at all :)

It's quite possible to do lockless smartpointers using atomic primitives, but it's no fun trying to make it portable, particularly across CPUs. It's quite easy if one sticks to using GCC though, it has atomic intrinsics.

here are the docs

[Edited by - penguinpusher on June 13, 2007 7:23:38 AM]
Lorien Dunn
Regarding atomic operations:

Windows:
InterlockedIncrement/InterlockedDecrement (http://msdn2.microsoft.com/en-us/library/ms683614.aspx)

Linux:
atomic_inc/atomic_dec (http://docs.sun.com/app/docs/doc/816-5168/6mbb3hr3n?a=view)


You should use these functions for reference counting.
I wish it were so simple :) That manpage you linked to is for Solaris rather than Linux, atomic stuff is one of those things that isn't standardised across unicies (hence the coolness of the gcc intrinsics). Linux has a bunch of different stuff with similar functionality, it's still in atomic.h, but it's not part of the c library. There is a separate atomic.h file full of inline asm for each architecture in the kernel headers, mine is at /usr/src/linux-headers-2.6.20-15/include/asm-i386/atomic.h

The functions aren't documented in any manpages I know of, and the atomic.h headers aren't needed for glibc. You have to have the full kernel headers packages installed to get them.
Lorien Dunn
For atomic operations, the libatomic_ops library from HP is interesting.

It contains implementations for a lot of platforms, including linux and windows. It's licensed under a MIT-style license.
Advertisement
Wow, thanks Gyrbo. Off to work to make my own SmartPointer classes lock free!

WitchLord I will have a go at hacking AngelScript handles to be lock free using this library once I've re done (been planning a re-write anyway) and tested my own SmartPointers and will post code if successful. Or if you'd prefer I'll do an svn patch. However I don't have a multiprocessor machine to test on atm.

If you haven't adapted the thread local storage code for AS I'll do it too, I was under the impression that AS used no mutexes/critical sections by means of using different interpreters in different threads and keeping instances of objects isolated between threads. I need a harder realtime scripting language than what AS currently is for low latency audio (~ 1 millisecond buffer sizes).
Lorien Dunn
Quote: Original post by penguinpusher
I need a harder realtime scripting language than what AS currently is for low latency audio (~ 1 millisecond buffer sizes).


penguinpusher... i need that too!
i've done some tests some months ago, and while AS perform very fast, sometimes it is just too slow for that purpose.

anyway, when u got something usable for realtime audio manipulation you can post a shout here

:)
When you do your modifications of AngelScript, keep in mind that the code cannot have any dependencies on external libraries. If you send me code I prefer that you send me a zip of your changed files, rather than a SVN patch. I'll use WinMerge to merge the code with the library and check in the code.

The only critical sections you'll have problem with in multithreaded applications are the ones already mentioned (i.e. the thread local storage for storing the currently active context, and the reference counters on the engine). You yourself have suggested the improvements on those so once that's implemented there should be no more critical sections, right?

Multithreaded programming and hard realtime applications are not my strong side, so feel free to make more suggestions. I'll try to adopt as much as possible that doesn't interfere with my goals for the library.

Regards,
Andreas




AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

There is also ZThread for threading/synchronization. I've adopted some code from there to my own applications.

http://zthread.sourceforge.net

This topic is closed to new replies.

Advertisement