PREV UP next StackThreads/MP: version 0.77 User's Guide

3.13: Thread and Worker ID

       extern long tls(worker_id);
@       long st_n_current_workers();
@       extern long tls(thread_id);

tls(worker_id) is an identifier of the calling worker within the worker group it belongs to. It holds a value from zero to n-1, where n is the number of workers withing the worker group the calling worker belongs to. When a worker group is created by stf_create_async_worker_group or stf_create_sync_worker_group, the entry function starts execution on the worker zero.

tls(thread_id) is a global identifier of the calling worker. It holds from zero to n-1, where n is the number of workers which have ever been created from the beginning of the program execution. The same identifier is (currently) not reused.

st_n_current_workers() returns the number of workers that currently belongs to the worker group that the calling worker belongs to. The worker group is initially created with the number of workers specified with the argument to stf_create_async_worker_group or stf_create_sync_worker_group (or the argument to -nw option in the case of the toplevel worker group) and may later be extended by st_add_worker(). The value returned by st_n_current_workers() at any moment may be smaller than the number of workers you have requested to create. For example, if you create a worker group that initially has 4 workers and performed st_add_worker() 3 times, st_n_current_workers() at a given moment may return any value from 1 to 7.

tls(worker_id) and tls(thread_id) can be used to guarantee that accesses to a single location do not occur concurrently. For example, the following code increments an element of a counter counters_array by one. The summation of all the elements in counters_array hold the total number of updates performed. Assuming counters_array is acceseed only from this worker group, there are no race conditions in counters_array because each access that may occur in parallel at runtime is given a unique value for tls(worker_id).

       x = compute_increment();
@       counters_array[tls(worker_id)] += x;

Note that the value of tls(worker_id) or tls(thread_id) may change even within a single function, as a result of thread suspension or migration. Therefore, in practice, it is always dangerous to assume that they are persistent across a procedure call (including ST_POLLING() and ST_STEAL_REQ_CHECK()). For example, you cannot manually optimize away tls(worker_id) by caching it to a local variable; the following code is probably unsafe.

       w = tls(worker_id);
@       x = compute_increment();
@       counters_array[w] += x;

Here, there are no guarantee that w at the two increments corresponds to the current worker id, because compute_increment may cause the rest of the computation to block or migrate.

The difference between tls(worker_id) and tls(thread_id) is important only when you create multiple worker groups within a program. However, it is recommended that you use tls(worker_id) where possible, because such programs are likely to be reusable.

Having said all, using tls(worker_id) or tls(thread_id) to gurantee freedom from races are not recommended at all. It is advisable to use a general synchronization method or a spin lock method, such as semaphore or st_read_and_lock_any. For example, the following code achieves the same effect without using tls(worker_id), given that counters_array is an array of st_int_loc_t.

       int v;
@       int x = compute_increment();
@       int idx = st_read_and_lock_any_int(counters_array, 
@                                          sizeof(st_int_loc_t), n, &v);
@       st_write_and_unlock_int(counters_array + idx, v + x);

It dynamically finds a location that is not locked. The size of counters_array is now arbitrary; it does not have to match the number of workers just to avoid out of range indices.