Thread: Thread locals api wrapper

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733

    Thread locals api wrapper

    When looking for the linux equivalents of TlsAlloc etc I found none, since nobody who uses the library I'm making would want to have to check what operating system they're on I've resorted to a wrapper that mimics it as best it can, wandering what you guys think of it. Can it be made better?
    Code:
    #	ifdef PAW_API_MSW
    #		define PAWTLS
    #		define PAWTLS_API_BASED
    #	elif (__GNUC__)
    #		define PAWTLS __thread
    #	elif defined( _MSC )
    #		define PAWTLS _declspec(thread)
    #	elif PAW_STDC >= 202300L
    #		include <threads.h>
    #		define PAWTLS thread_local
    #	elif PAW_STDC >= 201113L
    #		define PAWTLS _Thread_local
    #	else
    #		define PAWTLS
    #		define PAWTLS_NO_LOCALS
    #	endif
    Windows:
    Code:
    PAWAPP_API void*  pawtls_create(void) { return (void*)1; }
    PAWAPP_API pawvu  pawtls_newloc( void *tls, pawvu size )
    	{ (void)tls; (void)size; return TlsAlloc(); }
    PAWAPP_API void   pawtls_remloc( void *tls, pawvu i )
    	{ (void)tls; TlsFree(i); }
    PAWAPP_API void*  pawtls_getloc( void *tls, pawvu i )
    	{ (void)tls; return TlsGetValue(i); }
    PAWAPP_API void   pawtls_setloc( void *tls, pawvu i, void *ud )
    	{ (void)tls; return TlsSetValue( i, ud ); }
    PAWAPP_API void   pawtls_delete( void *tls ) { (void)tls; }
    Linux:
    Code:
    typedef struct _pawtls_entry { pawvu size; void *value; } pawtls_entry;
    PAWTLS pawbuf pawtls = {0};
    PAWAPP_API void*  pawtls_create( void ) { return &pawtls; }
    PAWAPP_API pawvu  pawtls_newloc( void *tls, pawvu size )
    {
    	pawtls_entry *list = pawbuf_gettop(pawtls), *ent = NULL;
    	pawvu i = 0, len = pawtls.len;
    	(void)tls;
    	if ( !size )
    		return PAWTLS_NO_INDICES;
    	if ( !list )
    	{
    		pawtls = pawbuf_newmax( sizeof(pawtls_entry), PAWCD_WIDTH );
    		ent = pawbuf_gettop(pawtls);
    		if ( !ent )
    			return PAWTLS_NO_INDICES;
    		ent->size = size;
    		return 0;
    	}
    	for ( ; i < len; ++i )
    	{
    		ent = list + i;
    		if ( ent->size )
    			continue;
    		ent->size;
    		return i;
    	}
    	list = pawbuf_fitmax( &pawtls, len * 2 );
    	if ( !list )
    		return PAWTLS_NO_INDICES;
    	ent = list + len;
    	ent->size = size;
    	pawtls.len++;
    	return len;
    }
    PAWAPP_API void   pawtls_remloc( void *tls, pawvu i )
    {
    	pawtls_entry *ent = pawbuf_offset( pawtls, i );
    	(void)tls;
    	if ( !ent )
    		return;
    	ent->size = 0;
    	ent->value = NULL;
    }
    PAWAPP_API void*  pawtls_getloc( void *tls, pawvu i )
    {
    	pawtls_entry *ent = pawbuf_offset(pawtls,i);
    	return ent ? ent->value : NULL;
    }
    PAWAPP_API void   pawtls_setloc( void *tls, pawvu i, void *ud )
    {
    	pawtls_entry *ent = pawbuf_offset( pawtls, i );
    	(void)tls;
    	if ( ent )
    		ent->value = ud;
    }
    PAWAPP_API void   pawtls_delete(void *tls)
    	{ (void)tls; pawbuf_expire(&pawtls); }
    Last edited by awsdert; 03-12-2023 at 05:03 AM. Reason: fixed typo in #elif statement

  2. #2
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Changed my mind about the wrapper after thinking of a better solution, just ignore tlsalloc altogether...unless I need it specifically for declaring the buffer a thread local.

    Code:
    typedef struct _pawtls_ent { pawexe tid; pawmem mem; } pawtls_ent;
    typedef struct _pawtls_buf { pawvu size; pawvu *addr; pawbuf buff; } pawtls_buf;
    PAWTLS pawbuf pawtls = {0};
    #			ifdef PAWTLS_IS_GLOBAL
    static pawsyn pawtls_syn = BAD_pawsyn;
    pawd  pawtls_init(void)
    	{ return pawsyn_init(&pawtls_syn,&pawtls,pawtls_class,1); }
    PAWAPP_QCK void* pawtls_wait(void) { return pawsyn_wait(&pawtls_syn); }
    PAWAPP_QCK void  pawtls_free(void) { pawsyn_free(&pawtls_syn); }
    void  pawtls_term(void) { pawsyn_term(&pawtls_syn); }
    #			else
    pawd  pawtls_init(void) { return 0; }
    PAWAPP_QCK void* pawtls_wait(void) { return &pawtls; }
    PAWAPP_QCK void  pawtls_free(void) {}
    void  pawtls_term(void) { pawbuf_expire(&pawtls); }
    #			endif
    PAWAPP_QCK pawtls_ent* pawtls_newmem( pawtls_buf *buff )
    {
    	pawexe tid = pawgetexe();
    	pawvu i = 0, len = buff->buff.len;
    	pawtls_ent **list = (pawtls_ent**)pawbuf_gettop(buff->buff), *ent = NULL;
    	for ( ; i < len; ++i )
    	{
    		ent = list[i];
    		if ( ent && ent->tid == tid )
    			return ent;
    	}
    	ent = pawnew(sizeof(pawtls_ent));
    	if ( !ent )
    		return PAWERR_OUT_OF_SPACE;
    	ent->tid = pawgetexe();
    	ent->mem = pawmem_newcap( buff->size, buff->size );
    	if ( !pawmem_gettop(ent->mem) )
    	{
    		pawdel( ent, sizeof(pawtls_ent) );
    		return PAWERR_OUT_OF_SPACE;
    	}
    	for ( i = 0; i < len; ++i )
    	{
    		if ( !list[i] )
    			return (list[i] = ent);
    	}
    	if ( list )
    		list = pawbuf_addlen( &(buff->buff), len );
    	else
    	{
    		buff->buff = pawbuf_newnum( sizeof(void*), PAWCD_WIDTH );
    		list = pawbuf_gettop(buff->buff);
    	}
    	if ( !list )
    	{
    		pawmem_expire(&(ent->mem));
    		pawdel( ent, sizeof(pawtls_ent) );
    		return PAWERR_OUT_OF_SPACE;
    	}
    	return (list[len] = ent);
    }
    PAWAPP_API pawd pawtls_getloc( void **dest, pawvu *addr, pawvu size )
    {
    	pawbuf *tls = pawtls_wait();
    	pawtls_ent *entry = NULL;
    	pawtls_buf *buffs = pawbuf_gettop(*tls), *buff = NULL;
    	pawvu i = 0, buffc = tls->len;
    	pawd err = 0;
    	*dest = NULL;
    	if ( !addr || !size )
    	{
    		pawtls_free();
    		return PAWERR_INVALID_DATA;
    	}
    	if ( !buffs )
    	{
    		*tls = pawbuf_newlen( sizeof(pawtls_buf), PAWCD_WIDTH );
    		buffs = pawbuf_gettop(*tls);
    		if ( !buffs )
    		{
    			err = tls->mem.err;
    			pawtls_free();
    			return err;
    		}
    	}
    	buffc = tls->len;
    	for ( i = *addr; i < buffc; ++i )
    	{
    		buff = buffs + i;
    		if ( buff->addr == addr )
    		{
    			err = (buff->size == size) ? 0 : PAWERR_INVALID_DATA;
    			if ( err )
    			{
    				pawtls_free();
    				return err;
    			}
    			entry = pawtls_newmem(buff);
    			pawtls_free();
    			if ( entry )
    			{
    				*dest = pawmem_gettop(entry->mem);
    				return 0;
    			}
    			return PAWERR_OUT_OF_SPACE;
    		}
    	}
    	for ( i = 0; i < buffc; ++i )
    	{
    		buff = buffs + i;
    		if ( buff->addr )
    			continue;
    		goto claim_buff;
    	}
    	buffs = pawbuf_addlen( tls, i );
    	if ( !buffs )
    	{
    		err = tls->mem.err;
    		pawtls_free();
    		return err;
    	}
    	buff = buffs + i;
    	claim_buff:
    	*addr = i;
    	buff->addr = addr;
    	buff->size = size;
    	entry = pawtls_newmem(buff);
    	if ( entry )
    	{
    		pawtls_free();
    		*dest = pawmem_gettop(entry->mem);
    		return 0;
    	}
    	buff->addr = NULL;
    	buff->size = 0;
    	*addr = 0;
    	return PAWERR_OUT_OF_SPACE;
    }
    PAWAPP_API void   pawtls_remloc( pawvu *addr )
    {
    	pawexe tid = pawgetexe();
    	pawbuf *tls = pawtls_wait();
    	pawtls_ent *list = NULL, *ent = NULL;
    	pawtls_buf *buff = pawbuf_offset( *tls, *addr );
    	pawvu i = 0, len = 0;
    	if ( !buff || buff->addr != addr )
    	{
    		pawtls_free();
    		return;
    	}
    	len = pawbuf_getlen(buff->buff);
    	for ( ; i < len; ++i )
    	{
    		ent = list[i];
    		if ( ent && ent->tid == tid )
    		{
    			pawmem_expire(&(ent->mem));
    			pawdel( ent, sizeof(pawtls_ent) );
    			list[i] = NULL;
    			break;
    		}
    	}
    	for ( i = 0; i < len; ++i )
    	{
    		if ( list[i] )
    		{
    			pawtls_free();
    			return;
    		}
    	}
    	buff->addr = NULL;
    	buff->size = NULL;
    	pawbuf_expire( &(buff->buff) );
    	pawtls_free();
    }
    PAWAPP_API void   pawtls_remall(void)
    {
    	// Not yet written, planning to remove all of current thread's pointers from the array, probably make use of my define to cleanup completely when locals are available
    }
    The basic idea is that can declare global indices normally with:

    Code:
    PAWTLS pawvu global_name = 0;
    void* thread( void*ud )
    {
    	foo_t *thread_ptr = NULL;
    	pawd err = pawtls_addloc( (void**)&thread_ptr, &global_name, sizeof(foo_t) );
    	if ( err )
    		return ud;
    	...
    	pawtls_remall();
    	return ud;
    }
    The thing about this design is that it's usable even for library functions that cannot/should not pass their indices back, the only thing that is needed is for the critical section to be initialized & destroyed in the main thread, unless someone has a better idea for forcing tls to be available even in strict C89/C99 I'll be going with this.

  3. #3
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    The Linux/posix equivalent of TLSAlloc and friends are pthread_key_create and pthread_get/setspecific

  4. #4
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Quote Originally Posted by adeyblue View Post
    The Linux/posix equivalent of TLSAlloc and friends are pthread_key_create and pthread_get/setspecific
    Thanks, having look at it I think I'll still go with a custom solution, was just about to post an improved version of my existing solution, realised I was taking the long route with buffers when linked lists would be faster, so now I'm using that:
    Code:
    typedef struct _pawtls_ent pawtls_ent;
    typedef struct _pawtls_buf pawtls_buf;
    struct _pawtls_ent { pawtls_ent *prv, *nxt; pawexe tid; void *data; };
    struct _pawtls_buf { pawtls_buf *prv, *nxt; pawvu size; pawvu *addr; pawtls_ent *top; };
    PAWTLS pawtls_buf *pawtls = NULL;
    #			ifdef PAWTLS_IS_GLOBAL
    static pawsyn pawtls_syn = BAD_pawsyn;
    pawd  pawtls_init(void)
    	{ return pawsyn_init(&pawtls_syn,(void*)&pawtls,pawtls_class,1); }
    PAWAPP_QCK pawtls_buf* pawtls_wait(void)
    	{ return *((pawtls_buf**)pawsyn_wait(&pawtls_syn)); }
    PAWAPP_QCK void  pawtls_free(void) { pawsyn_free(&pawtls_syn); }
    void  pawtls_term(void) { pawsyn_term(&pawtls_syn); }
    #			else
    pawd  pawtls_init(void) { return 0; }
    PAWAPP_QCK pawtls_buf* pawtls_wait(void) { return pawtls; }
    PAWAPP_QCK void  pawtls_free(void) {}
    void  pawtls_term(void) { pawbuf_expire(&pawtls); }
    #			endif
    static pawtls_ent* pawtls_newmem( pawtls_buf *buff )
    {
    	pawexe tid = pawgetexe();
    	pawtls_ent *prv = buff->top, *ent = prv;
    	for ( ; ent; prv = ent, ent = ent->nxt )
    	{
    		if ( ent->tid == tid )
    			return ent;
    	}
    	ent = pawnew(sizeof(pawtls_ent));
    	if ( !ent )
    		return PAWERR_OUT_OF_SPACE;
    	ent->tid = pawgetexe();
    	ent->nxt = buff->top;
    	ent->data = pawnew( buff->size );
    	if ( !pawmem_gettop(ent->data) )
    	{
    		pawdel( ent, sizeof(pawtls_ent) );
    		return NULL;
    	}
    	if ( buff->top )
    		buff->top->prv = ent;
    	buff->top = ent;
    	return ent;
    }
    PAWAPP_API pawd pawtls_getloc( void **dest, pawvu *addr, pawvu size )
    {
    	pawd err = 0;
    	pawtls_ent *entry = NULL;
    	pawtls_buf *buffs = pawtls_wait(), *buff = (void*)(*addr);
    	*dest = NULL;
    	if ( !size )
    	{
    		pawtls_free();
    		return PAWERR_INVALID_DATA;
    	}
    	if ( buff )
    	{
    		if ( buff->size != size )
    		{
    			pawtls_free();
    			return PAWERR_INVALID_DATA;
    		}
    		entry = pawtls_newmem(buff);
    		if ( !entry )
    			err = PAWERR_OUT_OF_SPACE;
    		else
    			*dest = entry->data;
    		pawtls_free();
    		return err;
    	}
    	buff = pawnew(sizeof(pawtls_buf));
    	if ( !buff )
    	{
    		pawtls_free(buff);
    		return PAWERR_OUT_OF_SPACE;
    	}
    	buff->addr = addr;
    	buff->size = size;
    	buff->nxt = buffs;
    	entry = pawtls_newmem(buff);
    	if ( !entry )
    	{
    		pawtls_free();
    		pawdel( buff, sizeof(pawtls_buf) );
    		return PAWERR_OUT_OF_SPACE;
    	}
    	*dest = entry->data;
    	if ( buffs )
    		buffs->prv = buff;
    	pawtls = buff;
    	pawtls_free();
    	return 0;
    }
    PAWAPP_API void   pawtls_remloc( pawvu *addr )
    {
    	pawexe tid = pawgetexe();
    	pawtls_ent *prv = NULL, *ent = NULL;
    	pawtls_buf *tls = pawtls_wait(), *buff = (void*)(*addr);
    	pawvu i = 0, len = 0;
    	if ( !buff || buff->addr != addr )
    	{
    		pawtls_free();
    		return;
    	}
    	prv = ent = buff->top;
    	for ( ; ent; prv = ent->nxt )
    	{
    		if ( ent->tid == tid )
    		{
    			prv->nxt = ent->nxt;
    			if ( ent->nxt )
    				ent->nxt->prv = ent->prv;
    			if ( buff->top == ent )
    				buff->top = ent->nxt;
    			break;
    		}
    	}
    	if ( !(buff->top) )
    	{
    		if ( buff->prv )
    			buff->prv->nxt = buff->nxt;
    		if ( buff->nxt )
    			buff->nxt->prv = buff->prv;
    		pawdel(buff);
    		*addr = 0;
    	}
    	pawtls_free();
    }
    PAWAPP_API void   pawtls_remall(void)
    {
    	pawtls_buf *buff = pawtls_wait(), temp = {NULL};
    	for ( ; buff; buff = temp.nxt )
    	{
    		temp = *buff;
    		pawtls_remloc( buff->addr );
    	}
    	pawtls_free();
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 11-20-2011, 12:01 AM
  2. Pointer to vector, scope and Locals
    By Lagrange in forum C++ Programming
    Replies: 6
    Last Post: 02-18-2010, 03:29 AM
  3. Static Locals VS Globals (Mem Location)
    By csonx_p in forum Windows Programming
    Replies: 2
    Last Post: 06-25-2008, 02:35 AM
  4. "c:/docume~1/owner/locals~1/temp/"
    By caroundw5h in forum Tech Board
    Replies: 4
    Last Post: 04-25-2004, 02:39 PM
  5. Globals, locals and prototypes
    By face_master in forum C++ Programming
    Replies: 1
    Last Post: 09-01-2001, 07:26 PM

Tags for this Thread