mirror of
https://github.com/cuberite/libevent.git
synced 2025-08-04 01:36:23 -04:00
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:
parent
d9a5515336
commit
e86af4b7e5
@ -104,6 +104,10 @@ struct bufferevent_rate_limit_group {
|
|||||||
/** Timeout event that goes off once a tick, when the bucket is ready
|
/** Timeout event that goes off once a tick, when the bucket is ready
|
||||||
* to refill. */
|
* to refill. */
|
||||||
struct event master_refill_event;
|
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
|
/** Lock to protect the members of this group. This lock should nest
|
||||||
* within every bufferevent lock: if you are holding this lock, do
|
* within every bufferevent lock: if you are holding this lock, do
|
||||||
* not assume you can lock another bufferevent. */
|
* not assume you can lock another bufferevent. */
|
||||||
|
@ -452,7 +452,7 @@ bev_group_random_element_(struct bufferevent_rate_limit_group *group)
|
|||||||
|
|
||||||
EVUTIL_ASSERT(! LIST_EMPTY(&group->members));
|
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);
|
bev = LIST_FIRST(&group->members);
|
||||||
while (which--)
|
while (which--)
|
||||||
|
@ -290,6 +290,9 @@ struct event_base {
|
|||||||
struct event th_notify;
|
struct event th_notify;
|
||||||
/** A function used to wake up the main thread from another thread. */
|
/** A function used to wake up the main thread from another thread. */
|
||||||
int (*th_notify_fn)(struct event_base *base);
|
int (*th_notify_fn)(struct event_base *base);
|
||||||
|
|
||||||
|
/* Saved seed for weak random number generator. */
|
||||||
|
ev_uint32_t weakrand_seed;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct event_config_entry {
|
struct event_config_entry {
|
||||||
|
23
evutil.c
23
evutil.c
@ -2273,14 +2273,23 @@ evutil_getenv_(const char *varname)
|
|||||||
return getenv(varname);
|
return getenv(varname);
|
||||||
}
|
}
|
||||||
|
|
||||||
long
|
ev_uint32_t
|
||||||
evutil_weakrand_(void)
|
evutil_weakrand_(ev_uint32_t* seed)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
*seed = ((*seed) * 1103515245 + 12345) & 0x7fffffff;
|
||||||
return rand();
|
return (*seed);
|
||||||
#else
|
}
|
||||||
return random();
|
|
||||||
#endif
|
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
|
int
|
||||||
|
2
poll.c
2
poll.c
@ -183,7 +183,7 @@ poll_dispatch(struct event_base *base, struct timeval *tv)
|
|||||||
if (res == 0 || nfds == 0)
|
if (res == 0 || nfds == 0)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
i = random() % nfds;
|
i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
|
||||||
for (j = 0; j < nfds; j++) {
|
for (j = 0; j < nfds; j++) {
|
||||||
int what;
|
int what;
|
||||||
if (++i == nfds)
|
if (++i == nfds)
|
||||||
|
2
select.c
2
select.c
@ -186,7 +186,7 @@ select_dispatch(struct event_base *base, struct timeval *tv)
|
|||||||
event_debug(("%s: select reports %d", __func__, res));
|
event_debug(("%s: select reports %d", __func__, res));
|
||||||
|
|
||||||
check_selectop(sop);
|
check_selectop(sop);
|
||||||
i = random() % nfds;
|
i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
|
||||||
for (j = 0; j < nfds; ++j) {
|
for (j = 0; j < nfds; ++j) {
|
||||||
if (++i >= nfds)
|
if (++i >= nfds)
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -699,6 +699,7 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
struct event *rev=NULL, *wev=NULL;
|
struct event *rev=NULL, *wev=NULL;
|
||||||
struct event_base *base = testdata->base;
|
struct event_base *base = testdata->base;
|
||||||
evutil_socket_t pair[2] = {-1, -1};
|
evutil_socket_t pair[2] = {-1, -1};
|
||||||
|
static ev_uint32_t seed = 123456789U;
|
||||||
|
|
||||||
/* This test is highly parameterized based on substrings of its
|
/* This test is highly parameterized based on substrings of its
|
||||||
* argument. The strings are: */
|
* argument. The strings are: */
|
||||||
@ -757,7 +758,7 @@ test_evbuffer_add_file(void *ptr)
|
|||||||
data = malloc(1024*512);
|
data = malloc(1024*512);
|
||||||
tt_assert(data);
|
tt_assert(data);
|
||||||
for (i = 0; i < datalen; ++i)
|
for (i = 0; i < datalen; ++i)
|
||||||
data[i] = (char)evutil_weakrand_();
|
data[i] = (char)evutil_weakrand_(&seed);
|
||||||
} else {
|
} else {
|
||||||
data = strdup("here is a relatively small string.");
|
data = strdup("here is a relatively small string.");
|
||||||
tt_assert(data);
|
tt_assert(data);
|
||||||
|
@ -829,6 +829,7 @@ test_evutil_rand(void *arg)
|
|||||||
char buf2[32];
|
char buf2[32];
|
||||||
int counts[256];
|
int counts[256];
|
||||||
int i, j, k, n=0;
|
int i, j, k, n=0;
|
||||||
|
static ev_uint32_t seed = 12346789U;
|
||||||
|
|
||||||
memset(buf2, 0, sizeof(buf2));
|
memset(buf2, 0, sizeof(buf2));
|
||||||
memset(counts, 0, sizeof(counts));
|
memset(counts, 0, sizeof(counts));
|
||||||
@ -836,8 +837,8 @@ test_evutil_rand(void *arg)
|
|||||||
for (k=0;k<32;++k) {
|
for (k=0;k<32;++k) {
|
||||||
/* Try a few different start and end points; try to catch
|
/* Try a few different start and end points; try to catch
|
||||||
* the various misaligned cases of arc4random_buf */
|
* the various misaligned cases of arc4random_buf */
|
||||||
int startpoint = evutil_weakrand_() % 4;
|
int startpoint = evutil_weakrand_(&seed) % 4;
|
||||||
int endpoint = 32 - (evutil_weakrand_() % 4);
|
int endpoint = 32 - (evutil_weakrand_(&seed) % 4);
|
||||||
|
|
||||||
memset(buf2, 0, sizeof(buf2));
|
memset(buf2, 0, sizeof(buf2));
|
||||||
|
|
||||||
|
@ -254,7 +254,8 @@ int evutil_resolve_(int family, const char *hostname, struct sockaddr *sa,
|
|||||||
|
|
||||||
const char *evutil_getenv_(const char *name);
|
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
|
/* Evaluates to the same boolean value as 'p', and hints to the compiler that
|
||||||
* we expect this value to be false. */
|
* we expect this value to be false. */
|
||||||
|
@ -326,7 +326,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (win32op->readset_out->fd_count) {
|
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) {
|
for (j=0; j<win32op->readset_out->fd_count; ++j) {
|
||||||
if (++i >= win32op->readset_out->fd_count)
|
if (++i >= win32op->readset_out->fd_count)
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -335,7 +336,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (win32op->exset_out->fd_count) {
|
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) {
|
for (j=0; j<win32op->exset_out->fd_count; ++j) {
|
||||||
if (++i >= win32op->exset_out->fd_count)
|
if (++i >= win32op->exset_out->fd_count)
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -345,7 +347,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv)
|
|||||||
}
|
}
|
||||||
if (win32op->writeset_out->fd_count) {
|
if (win32op->writeset_out->fd_count) {
|
||||||
SOCKET s;
|
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) {
|
for (j=0; j<win32op->writeset_out->fd_count; ++j) {
|
||||||
if (++i >= win32op->writeset_out->fd_count)
|
if (++i >= win32op->writeset_out->fd_count)
|
||||||
i = 0;
|
i = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user