Change evutil_weakrand_() to avoid platform random()

This change allows us to avoid perturbing the platform's random(), and
to avoid hitting locks on random() in the platform's libc.

evutil_weakrand_() is, well, weak, so we choose here an algorithm that
favors speed over a number of other possibly desirable properties.
We're using a linear congruential generator, and taking our parameters
from those shared by the OpenBSD random() implementation, and
Glibc's fastest random() implementation.

The low bits of a LCG of modulus 2^32 are (notoriously) less random
than the higher bits.  So to generate a random value in a range, using
the % operator is no good; we ought to divide.  We add an
evutil_weakrand_range_() function to do that.

This code also changes the interface of evutil_weakrand_() so that it
now manipulates an explicit seed, rather than having the seed in a
static variable.  This change enables us to use existing locks to
achieve thread-safety, rather than having to rely on an additional lock.

(Patch by Nicholas Marriott; commit message by Nick Mathewson.)
This commit is contained in:
Nicholas Marriott 2012-04-09 10:46:32 -04:00 committed by Nick Mathewson
parent d9a5515336
commit e86af4b7e5
10 changed files with 39 additions and 17 deletions

View File

@ -104,6 +104,10 @@ struct bufferevent_rate_limit_group {
/** Timeout event that goes off once a tick, when the bucket is ready
* to refill. */
struct event master_refill_event;
/** Seed for weak random number generator. */
ev_uint32_t weakrand_seed;
/** Lock to protect the members of this group. This lock should nest
* within every bufferevent lock: if you are holding this lock, do
* not assume you can lock another bufferevent. */

View File

@ -452,7 +452,7 @@ bev_group_random_element_(struct bufferevent_rate_limit_group *group)
EVUTIL_ASSERT(! LIST_EMPTY(&group->members));
which = evutil_weakrand_() % group->n_members;
which = evutil_weakrand_range_(&group->weakrand_seed, group->n_members);
bev = LIST_FIRST(&group->members);
while (which--)

View File

@ -290,6 +290,9 @@ struct event_base {
struct event th_notify;
/** A function used to wake up the main thread from another thread. */
int (*th_notify_fn)(struct event_base *base);
/* Saved seed for weak random number generator. */
ev_uint32_t weakrand_seed;
};
struct event_config_entry {

View File

@ -2273,14 +2273,23 @@ evutil_getenv_(const char *varname)
return getenv(varname);
}
long
evutil_weakrand_(void)
ev_uint32_t
evutil_weakrand_(ev_uint32_t* seed)
{
#ifdef _WIN32
return rand();
#else
return random();
#endif
*seed = ((*seed) * 1103515245 + 12345) & 0x7fffffff;
return (*seed);
}
ev_uint32_t
evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top)
{
ev_uint32_t divisor, result;
divisor = EV_INT32_MAX / top;
do
result = evutil_weakrand_(seed) / divisor;
while (result > top);
return result;
}
int

2
poll.c
View File

@ -183,7 +183,7 @@ poll_dispatch(struct event_base *base, struct timeval *tv)
if (res == 0 || nfds == 0)
return (0);
i = random() % nfds;
i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
for (j = 0; j < nfds; j++) {
int what;
if (++i == nfds)

View File

@ -186,7 +186,7 @@ select_dispatch(struct event_base *base, struct timeval *tv)
event_debug(("%s: select reports %d", __func__, res));
check_selectop(sop);
i = random() % nfds;
i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
for (j = 0; j < nfds; ++j) {
if (++i >= nfds)
i = 0;

View File

@ -699,6 +699,7 @@ test_evbuffer_add_file(void *ptr)
struct event *rev=NULL, *wev=NULL;
struct event_base *base = testdata->base;
evutil_socket_t pair[2] = {-1, -1};
static ev_uint32_t seed = 123456789U;
/* This test is highly parameterized based on substrings of its
* argument. The strings are: */
@ -757,7 +758,7 @@ test_evbuffer_add_file(void *ptr)
data = malloc(1024*512);
tt_assert(data);
for (i = 0; i < datalen; ++i)
data[i] = (char)evutil_weakrand_();
data[i] = (char)evutil_weakrand_(&seed);
} else {
data = strdup("here is a relatively small string.");
tt_assert(data);

View File

@ -829,6 +829,7 @@ test_evutil_rand(void *arg)
char buf2[32];
int counts[256];
int i, j, k, n=0;
static ev_uint32_t seed = 12346789U;
memset(buf2, 0, sizeof(buf2));
memset(counts, 0, sizeof(counts));
@ -836,8 +837,8 @@ test_evutil_rand(void *arg)
for (k=0;k<32;++k) {
/* Try a few different start and end points; try to catch
* the various misaligned cases of arc4random_buf */
int startpoint = evutil_weakrand_() % 4;
int endpoint = 32 - (evutil_weakrand_() % 4);
int startpoint = evutil_weakrand_(&seed) % 4;
int endpoint = 32 - (evutil_weakrand_(&seed) % 4);
memset(buf2, 0, sizeof(buf2));

View File

@ -254,7 +254,8 @@ int evutil_resolve_(int family, const char *hostname, struct sockaddr *sa,
const char *evutil_getenv_(const char *name);
long evutil_weakrand_(void);
ev_uint32_t evutil_weakrand_(ev_uint32_t* seed);
ev_uint32_t evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top);
/* Evaluates to the same boolean value as 'p', and hints to the compiler that
* we expect this value to be false. */

View File

@ -326,7 +326,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
}
if (win32op->readset_out->fd_count) {
i = rand() % win32op->readset_out->fd_count;
i = evutil_weakrand_range_(&base->weakrand_seed,
win32op->readset_out->fd_count);
for (j=0; j<win32op->readset_out->fd_count; ++j) {
if (++i >= win32op->readset_out->fd_count)
i = 0;
@ -335,7 +336,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
}
}
if (win32op->exset_out->fd_count) {
i = rand() % win32op->exset_out->fd_count;
i = evutil_weakrand_range_(&base->weakrand_seed,
win32op->exset_out->fd_count);
for (j=0; j<win32op->exset_out->fd_count; ++j) {
if (++i >= win32op->exset_out->fd_count)
i = 0;
@ -345,7 +347,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
}
if (win32op->writeset_out->fd_count) {
SOCKET s;
i = rand() % win32op->writeset_out->fd_count;
i = evutil_weakrand_range_(&base->weakrand_seed,
win32op->writeset_out->fd_count);
for (j=0; j<win32op->writeset_out->fd_count; ++j) {
if (++i >= win32op->writeset_out->fd_count)
i = 0;