A Thread Safe ISAAC-rand
02 March, 2018
ISAAC-rand is not thread safe because it uses a global context to keep track of the random state, meaning that when the context is seeded in a threaded environment, its state is likely to be clobbered by another thread. A lockless solution to this issue can be achieved with a few relatively simple changes.
Looking into ISAAC’s source code (ISAAC-rand.c
), we notice that the random context is actually scoped quite locally, shared only between seed_random
and random_num
:
randctx R;
void seed_random(char* term, int length)
{
memset(R.randrsl, 0, sizeof(R.randrsl));
strncpy((char *)(R.randrsl), term, length);
randinit(&R, TRUE);
}
short random_num(short max)
{
return rand(&R) % max;
}
Studying the above snippet, we can simply create a local version of R
in seed_random
, and modify the function to return the indicated context. In this way, any context can be maintained unharmed, and localized to its own function.
To facilitate this change, the random_num
function should also be modified to take a local version of the context. The most straightforward way of accomplishing this is to change the function’s signature to take the random context in form of an argument;
randctx seed_random(char* term, int length)
{
randctx R;
memset(R.randrsl, 0, sizeof(R.randrsl));
strncpy((char *)(R.randrsl), term, length);
randinit(&R, TRUE);
return R;
}
short random_num(randctx *R, short max)
{
return rand(R) % max;
}
Note that this changes the program’s original design; it is now required that the context be initialized with a seed before returning random numbers. To get around this, a solution may be to create a basic function, perhaps named init_random
, that returns a randctx
.
Additionally, we of course have to change the way that the random number generator is used. Below are two implementations: before (top), and after (bottom).
// Before.
void example_function(char* term)
{
...
seed_random(term, WORDLEN);
short rand = random_num(SIGNATURE_LEN);
...
}
// After.
typedef struct randctx randctx;
...
void example_function(char* term)
{
...
randctx R = seed_random(term, WORDLEN);
short rand = random_num(&R, SIGNATURE_LEN);
...
}
Depending on how you’ve setup your code, you’ll need to add a type definition for randctx
so that the compiler will recognize it, as seen above.