Names specified here
Name Description Notes Source Availability
_Thread_local Storage class for thread-local variables L S Keyword C11
TSS_DTOR_ITERATIONS Maximum number of destructor invocations ? M <threads.h> C11
thread_local Storage class for thread-local variables ? M S <threads.h> C11
tss_create() Create thread-specific storage ? (·) <threads.h> C11
tss_delete() Destroy thread-specific storage ? (·) <threads.h> C11
tss_dtor_t Destructor type for thread-specific storage ? T <threads.h> C11
tss_get() Read thread-specific storage ? (·) <threads.h> C11
tss_set() Write thread-specific storage ? (·) <threads.h> C11
tss_t Thread-specific storage identifier type ? T <threads.h> C11

Names matching ^tss_[a-z] might be added to <threads.h>.

An object with thread storage duration is really a set of objects, each one accessible to a different thread, but all identified as the same object. Such objects are also known as thread locals, and are declared with the keyword _Thread_local or the macro thread_local, which expands to _Thread_local. We'll call the actual objects ‘instances’, and each one is owned by the thread that accesses it. For example:

thread_local int counter;

int get_counter(void)
{
  return counter;
}

void do_something(void)
{
  counter++;

  . . .
}

Can static be used with thread_local?

Can thread_local appear in a block?

Can the address of a thread_local be got?

counter allows any thread to keep track of how many times it has invoked do_something. There is no need for any mutex to access the variable safely, as each thread can only access its own instance.

An instance is created the first time the owning thread accesses the thread_local object. It is initialized with the value provided in the declaration, if present. Otherwise, it takes on the appropriate null value for the type.

Another mechanism exists, called thread-specific storage, which permits destructors to be associated with thread-local data, so that the instances of the data will be properly discarded when a thread terminates. The type tss_t identifies an item of thread-specific storage, whose instances have the type void *.

#include <threads.h>
typedef void (*tss_dtor_t)(void *);
int tss_create(tss_t *kp, tss_dtor_t dtor);

tss_create creates a thread-specific object. On success, it assigns the identifier for the object to *kp, and returns thrd_success. Otherwise, it returns thrd_error.

The initial value for each instance of the object is NULL.

dtor identifies a destructor. The current value v of an instance will be passed to (*dtor)(v) when the owning thread is terminating, if dtor is not 0, and v is not NULL. The current value is reset to NULL before calling the destructor. After the destructor returns, the current value is checked again to see if it is still NULL. If not, the destruction process repeats. This occurs upto TSS_DTOR_ITERATIONS times. The owning thread calls the destructor.

#include <threads.h>
void *tss_get(tss_t k);
int tss_set(tss_t k, void *v);

tss_get gets the current value of the instance of object k for the current thread. Note that it returns NULL on failure, but it will also return NULL if that happens to be the current value, and there is no way to distinguish between these two events.

tss_set sets the current value to v, [Does it call the destructor then for the old value?] and returns thrd_success, or thrd_error on failure. Unless you're sure that the old value is not NULL, you should first call tss_get to obtain it and deallocate it, and then call tss_set with the new value.

#include <threads.h>
void tss_delete(tss_t k);

tss_delete releases resources allocated to the thread-specific object k. This does not invoke the destructor on any instance, so it should not be called unless all instances are known to be NULL or otherwise require no destruction.

Example

[ Work in progress : Hmm, don't think I've actually tried this code out!]

Given a type foo_t acting as a handle for some sort of context, a function foo_createctxt to create a context, and foo_destroyctxt to destroy a context, define a new function foo_getctxt to create a context for the calling thread on demand, or return a previously created one. Each context is destroyed when its thread terminates.

#include <stdbool.h>
#include <threads.h>

typedef struct foo_ctxt *foo_t;

foo_t foo_createctxt(void);
void foo_destroyctxt(foo_t);

static void destruct(void *p)
{
  foo_destroyctxt(tss_get(ctxt));
}

static tss_t ctxt;
static bool ctxt_error;

static void init(void)
{
  if (tss_create(&ctxt, &destruct) == thread_error)
    ctxt_error = true;
}

/* Get or create a foo_t for the calling thread. */
foo_t foo_getctxt(void)
{
  static once_flag inited = ONCE_FLAG_INIT;
  call_once(&inited, &init);

  if (ctxt_error) return NULL;
  foo_t res = tss_get(ctxt);
  if (res != NULL) return res;
  res = foo_createctxt();
  if (res == NULL) return NULL;
  if (tss_set(ctxt, res) == thread_error) {
    foo_destroyctxt(res);
    return NULL;
  }
  return res;
}

CHaR
Sitemap Supported
Site format updated 2024-06-05T22:37:07.391+0000
Data updated 1970-01-01T00:00:00.000+0000
Page updated 2023-10-04T20:24:03.209+0000