Re: Thread Safety - test_and_set primitive included
| From: | Ed | Date: | Wed, 07 May 2003 02:40:39 +0000 |
| Subject: | Re: Thread Safety - test_and_set primitive included | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-1329@lists.php.net to get a copy of this message | ||
Dear folks,
Forgive me for barging in, but I'm using a snippet of code that might be of
interest.
It is a "test and set" function to be used for locking resources across
threads. I was looking at the TSRM implementation (including the Mutexes)
and it did do quite what i needed -- things are not atomic. So here are a
couple of primitives in VC++ (Windows) and gcc (Linux) for the i386
platform. The Linux piece was "stolen" from /usr/include/asm/bitops.h --
originally written by Linus Torvalds. I "ported" it to VC++. With these 2
inline primitives people developing extensions should be able to implement
thread shared resources with safety -- provided one does not make mistakes
;-)
They seem to work -- no guarantees though :-)
There is an example of usage below, but one can come up a number of
variations for the "wrappers" based on the "inlines".
Thanks for the great tool!
All the best,
Ed Hoo
ed000001@hotmail.com
*** MY_FILE.H ***
/* ------------------------------------------------------- *\
* Primitives for a simple mutex implementation
* ------------------------------------------------------- */
#define ADDR (*(volatile long *) addr)
#ifdef WIN32
static __forceinline int test_and_set(int nr, volatile void *addr)
{
__asm
{
mov eax, nr
mov ebx, addr
lock bts [ebx], eax
sbb eax, eax
}
/* Return with result in EAX */
}
static __forceinline int test_and_clear(int nr, volatile void *addr)
{
__asm
{
mov eax, nr
mov ebx, addr
lock btr [ebx], eax
sbb eax, eax
}
/* Return with result in EAX */
}
#else
static __inline__ int test_and_set(int nr, volatile void *addr)
{
int oldbit;
__asm__ __volatile__( "lock ; "
"btsl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
:"Ir" (nr) : "memory");
return oldbit;
}
static __inline__ int test_and_clear(int nr, volatile void *addr)
{
int oldbit;
__asm__ __volatile__( "lock ; "
"btrl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
:"Ir" (nr) : "memory");
return oldbit;
}
#endif
*** MY_FILE.C ***
/* ------------------------------------------------------- *\
* mutex_lock(int resource_id)
*
* Waits until resource is avaliable, locks it and then returns
* ------------------------------------------------------- */
void mutex_lock(int n_resource_id)
{
if ( n_resource_id < 0 || n_resource_id > 31 ) return;
while ( test_and_set(n_resource_id, &ng_mutex) ) Sleep(1);
}
/* ------------------------------------------------------- *\
* mutex_unlock(int resource_id)
*
* Unlocks a resource, we assumed it's been called by the resource "locker"
* ------------------------------------------------------- */
void mutex_unlock(int n_resource_id)
{
if ( n_resource_id < 0 || n_resource_id > 31 ) return;
test_and_clear(n_resource_id, &ng_mutex);
}
/* ------------------------------------------------------- *\
* Mutexes -- up to 32 "mutexes" in ng_mutex [0..31]
* ------------------------------------------------------- */
static unsigned int ng_mutex = 0;
#define MUTEX_FIELD_HASH 0
/* ------------------------------------------------------- *\
* Variables needing thread safety
* ------------------------------------------------------- */
static t_field_hash* pg_field_hash = NULL;
static int pg_field_hash_references = 0;
/* ------------------------------------------------------- *\
* PHP_MINIT_FUNCTION
* ------------------------------------------------------- */
PHP_MINIT_FUNCTION(dvdaf)
{
mutex_lock(MUTEX_FIELD_HASH);
{
if ( ! pg_field_hash )
{
pg_field_hash = malloc(gp_field_count * sizeof(t_field_hash));
pg_field_hash_references = 1;
// calculate hash
}
else
{
pg_field_hash_references++;
}
}
mutex_unlock(MUTEX_FIELD_HASH);
return SUCCESS;
}
/* ------------------------------------------------------- *\
* PHP_MSHUTDOWN_FUNCTION
* ------------------------------------------------------- */
PHP_MSHUTDOWN_FUNCTION(dvdaf)
{
mutex_lock(MUTEX_FIELD_HASH);
{
if ( --pg_field_hash_references <= 0 )
{
free(pg_field_hash);
pg_field_hash = NULL;
pg_field_hash_references = 0;
}
}
mutex_unlock(MUTEX_FIELD_HASH);
return SUCCESS;
}