StackThreads/MP: version 0.77 User's Guide
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.