Re: Thread specific data for GLib
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list redhat com
- Subject: Re: Thread specific data for GLib
- Date: 08 Dec 1998 09:27:03 -0500
Sebastian Wilhelmi <wilhelmi@ira.uka.de> writes:
> > In general, I'd like to stick to something a lot closer
> > to the metal. All this StaticPrivate stuff may, IMO,
> > is really over-complicated, and may not match the
> > needs of the application.
>
> It is complicated for two reasons:
>
> 1) it has an own managment of data, this is to provide the possibility to
> destroy all data connected to a key. This can't be done on either
> solaris
> or posix.
> 2) it provides the additional parameter constructor, that is called,
> whenever the data, that would be returned is NULL. This is
> extremly practical, because most of the time you'll find yourself doing
> something like this:
>
> if( (data = g_private_get( key ) ) == NULL )
> g_private_set( key, data = g_new0(blablub,1) );
>
> This addition doesn't impose much overhead (it's just one comparison
> for
> every call, compared to the mutex lock it's just nothing).
>
> > The question, though, is how to provide a convenient
> > interface to getting a unique thread index to use
> > for your a bit of static data, without requiring
> > locking on each fetch of the static data.
>
> That simply is not possible without magic (like constructors and the
> like). Period. (Regarding pthread_once see below)
Not so fast. If you preallocate a single TSD key in
g_thread_init(), it is possible to have thread-specific
data that:
1) Does not need to lock a mutex for either get or set.
2) Is completely portable, assuming that there is
some integral type that is read/written atomically.
3) Is quite convenient to use.
The interface is:
static guint myindex = 0;
setspecific_once (&myindex, avalue);
[...]
somevalue = getspecific_once (&myindex);
Implementation below. The extension to
setspecific_once (guint *, GDestroyNotify) is
straightforward; it could be made prettier
with a few typedefs/#define's
[
You could also use the same basic strategy to build
a non-locking version of your GStaticMutex, but
I'm not convinced yet that the constructor
functionality is worth the complexity
]
> > Netscape generally uses constructors to set up the
> > the TSD indices; using pthreads you can use
> > pthread_once().
>
> here is a small test, I've done:
> pthread_mutex need 1031 nanoseconds for enter/exit pair.
> pthread_once needs 1327 nanoseconds for call.
> pthread_getspecific needs 3253 nanoseconds for call.
> pthread_setspecific needs 467 nanoseconds for call.
>
> The program is attached for anyone to try it on his/her platform. I really
> think, its rediculous, that pthread_setspecific is more than 5 times
> faster than pthread_getspecific, and still more than twice as fast as a
> pthread_mutex enter/exit pair. as you see pthread_once is slower than a
> pthread_mutex enter/exit pair. The last should actually also hold true on
> linux, I suppose.
I think it is ridiculous that Sun can't write a decent thread
library ;-) On LinuxThreads, the numbers are:
pthread_mutex need 382 nanoseconds for enter/exit pair.
pthread_once needs 96 nanoseconds for call.
pthread_getspecific needs 120 nanoseconds for call.
pthread_setspecific needs 152 nanoseconds for call.
[ glibc-2.0.7, PII/300, but the relative timings hold on
slower machines ]
pthread_once uses the double-checking idiom that
we rejected as being not necessarily portable -
so it's possible that Solaris is always locking
a mutex in its implementation of pthread_once().
But the LinuxThreads numbers for get/setspecific
I think should be typical for a decent package
as compared to locking a mutex.
for comparison, my getspecific_once and setspecific_once
functions take 155 and 205 nanoseconds/call
respectively... so getspecific_once should be (on LinuxThreads)
at least 4 times as fast as your StaticMutex
implementation.
Regards,
Owen
====
void
init_specific ()
{
tsd_key = pthread_key_create(&tsd_key, NULL);
tsd_pointer_hash = g_hash_table_new (g_direct_hash, NULL);
}
gpointer
getspecific_once (guint *index)
{
GArray *array;
static guint next_index = 0;
array = pthread_getspecific(tsd_key);
if (!array)
return NULL;
if (!*index)
return NULL;
/* Since the write to *index is assummed atomic, since
* *index is nonzero, it must have the correct value.
*/
else if (*index <= array->len)
return g_array_index (array, gpointer, (*index - 1));
else
return NULL;
}
void
setspecific_once (guint *index, gpointer data)
{
GArray *array;
static guint next_index = 0;
array = pthread_getspecific(tsd_key);
if (!array)
{
array = g_array_new (FALSE, FALSE, sizeof(gpointer));
pthread_setspecific(tsd_key, array);
}
if (!*index)
{
guint value;
pthread_mutex_lock (&tsd_mutex);
value = GPOINTER_TO_UINT (g_hash_table_lookup(tsd_pointer_hash, index));
if (!value)
{
value = ++next_index;
g_hash_table_insert (tsd_pointer_hash, index,
GUINT_TO_POINTER (value));
}
*index = value;
pthread_mutex_unlock (&tsd_mutex);
}
if (*index > array->len)
g_array_set_size (array, *index);
g_array_index (array, gpointer, (*index - 1)) = data;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]