Re: Thread Safety - test_and_set primitive included

From: 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; }

« previous php.internals (#1329) next »