Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
test: fix race conditions in test-async
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoordhuis committed Apr 17, 2012
1 parent d5e7786 commit 5345ee3
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 239 deletions.
24 changes: 0 additions & 24 deletions test/runner-unix.c
Expand Up @@ -285,30 +285,6 @@ void rewind_cursor() {
}


typedef void* (*uv_thread_cb)(void* arg);


uintptr_t uv_create_thread(void (*entry)(void* arg), void* arg) {
pthread_t t;
uv_thread_cb cb = (uv_thread_cb)entry;
int r = pthread_create(&t, NULL, cb, arg);

if (r) {
return 0;
}

return (uintptr_t)t;
}


/* Wait for a thread to terminate. Should return 0 if the thread ended, -1 on
* error.
*/
int uv_wait_thread(uintptr_t thread_id) {
return pthread_join((pthread_t)thread_id, NULL);
}


/* Pause the calling thread for a number of milliseconds. */
void uv_sleep(int msec) {
usleep(msec * 1000);
Expand Down
63 changes: 0 additions & 63 deletions test/runner-win.c
Expand Up @@ -274,69 +274,6 @@ void rewind_cursor() {
}


typedef struct {
void (*entry)(void* arg);
void* arg;
} thread_info_t;


static unsigned __stdcall create_thread_helper(void* info) {
/* Copy thread info locally, then free it */
void (*entry)(void* arg) = ((thread_info_t*) info)->entry;
void* arg = ((thread_info_t*) info)->arg;

free(info);

/* Run the actual thread proc */
entry(arg);

/* Finalize */
_endthreadex(0);
return 0;
}


/* Create a thread. Returns the thread identifier, or 0 on failure. */
uintptr_t uv_create_thread(void (*entry)(void* arg), void* arg) {
uintptr_t result;
thread_info_t* info;

info = (thread_info_t*) malloc(sizeof *info);
if (info == NULL) {
return 0;
}

info->entry = entry;
info->arg = arg;

result = _beginthreadex(NULL,
0,
&create_thread_helper,
(void*) info,
0,
NULL);

if (result == 0) {
free(info);
return 0;
}

return result;
}


/* Wait for a thread to terminate. Should return 0 if the thread ended, -1 on
* error.
*/
int uv_wait_thread(uintptr_t thread_id) {
if (WaitForSingleObject((HANDLE)thread_id, INFINITE) != WAIT_OBJECT_0) {
return -1;
}

return 0;
}


/* Pause the calling thread for a number of milliseconds. */
void uv_sleep(int msec) {
Sleep(msec);
Expand Down
8 changes: 0 additions & 8 deletions test/task.h
Expand Up @@ -103,14 +103,6 @@ typedef enum {
int run_helper_##name()


/* Create a thread. Returns the thread identifier, or 0 on failure. */
uintptr_t uv_create_thread(void (*entry)(void* arg), void* arg);

/* Wait for a thread to terminate. Should return 0 if the thread ended, -1 on
* error.
*/
int uv_wait_thread(uintptr_t thread_id);

/* Pause the calling thread for a number of milliseconds. */
void uv_sleep(int msec);

Expand Down
186 changes: 42 additions & 144 deletions test/test-async.c
Expand Up @@ -24,192 +24,90 @@
#include <stdio.h>
#include <stdlib.h>

static uv_thread_t thread;
static uv_mutex_t mutex;

static uv_prepare_t prepare_handle;
static uv_prepare_t prepare;
static uv_async_t async;

static uv_async_t async1_handle;
/* static uv_handle_t async2_handle; */
static volatile int async_cb_called;
static int prepare_cb_called;
static int close_cb_called;

static int prepare_cb_called = 0;

static volatile int async1_cb_called = 0;
static int async1_closed = 0;
/* static volatile int async2_cb_called = 0; */

static int close_cb_called = 0;

static uintptr_t thread1_id = 0;
#if 0
static uintptr_t thread2_id = 0;
static uintptr_t thread3_id = 0;
#endif


/* Thread 1 makes sure that async1_cb_called reaches 3 before exiting. */
void thread1_entry(void *arg) {
uv_sleep(50);

while (1) {
switch (async1_cb_called) {
case 0:
uv_async_send(&async1_handle);
break;

case 1:
uv_async_send(&async1_handle);
break;

case 2:
uv_async_send(&async1_handle);
break;

default:
return;
}
}
}

#if 0
/* Thread 2 calls uv_async_send on async_handle_2 8 times. */
void thread2_entry(void *arg) {
int i;

while (1) {
switch (async1_cb_called) {
case 0:
uv_async_send(&async2_handle);
break;

case 1:
uv_async_send(&async2_handle);
break;

case 2:
uv_async_send(&async2_handle);
break;
}
uv_sleep(5);
}

if (async1_cb_called == 20) {
uv_close(handle);
}
}

void thread_cb(void *arg) {
int n;
int r;

/* Thread 3 calls uv_async_send on async_handle_2 8 times
* after waiting half a second first.
*/
void thread3_entry(void *arg) {
int i;
do {
uv_mutex_lock(&mutex);
n = async_cb_called;
uv_mutex_unlock(&mutex);

for (i = 0; i < 8; i++) {
uv_async_send(&async2_handle);
r = uv_async_send(&async);
ASSERT(r == 0);
}
while (n < 3);
}
#endif


static void close_cb(uv_handle_t* handle) {
ASSERT(handle != NULL);
close_cb_called++;
}


static void async1_cb(uv_async_t* handle, int status) {
ASSERT(handle == &async1_handle);
ASSERT(status == 0);

async1_cb_called++;
printf("async1_cb #%d\n", async1_cb_called);
static void async_cb(uv_async_t* handle, int status) {
int n;

if (async1_cb_called > 2 && !async1_closed) {
async1_closed = 1;
uv_close((uv_handle_t*)handle, close_cb);
}
}


#if 0
static void async2_cb(uv_handle_t* handle, int status) {
ASSERT(handle == &async2_handle);
ASSERT(handle == &async);
ASSERT(status == 0);

async2_cb_called++;
printf("async2_cb #%d\n", async2_cb_called);
uv_mutex_lock(&mutex);
n = ++async_cb_called;
uv_mutex_unlock(&mutex);

if (async2_cb_called == 16) {
uv_close(handle);
if (n == 3) {
uv_close((uv_handle_t*)&async, close_cb);
uv_close((uv_handle_t*)&prepare, close_cb);
}
}
#endif


static void prepare_cb(uv_prepare_t* handle, int status) {
ASSERT(handle == &prepare_handle);
int r;

ASSERT(handle == &prepare);
ASSERT(status == 0);

switch (prepare_cb_called) {
case 0:
thread1_id = uv_create_thread(thread1_entry, NULL);
ASSERT(thread1_id != 0);
break;

#if 0
case 1:
thread2_id = uv_create_thread(thread2_entry, NULL);
ASSERT(thread2_id != 0);
break;

case 2:
thread3_id = uv_create_thread(thread3_entry, NULL);
ASSERT(thread3_id != 0);
break;
#endif

case 1:
uv_close((uv_handle_t*)handle, close_cb);
break;

default:
FATAL("Should never get here");
}
if (prepare_cb_called++)
return;

prepare_cb_called++;
r = uv_thread_create(&thread, thread_cb, NULL);
ASSERT(r == 0);
uv_mutex_unlock(&mutex);
}


TEST_IMPL(async) {
int r;

r = uv_prepare_init(uv_default_loop(), &prepare_handle);
ASSERT(r == 0);
r = uv_prepare_start(&prepare_handle, prepare_cb);
r = uv_mutex_init(&mutex);
ASSERT(r == 0);
uv_mutex_lock(&mutex);

r = uv_async_init(uv_default_loop(), &async1_handle, async1_cb);
r = uv_prepare_init(uv_default_loop(), &prepare);
ASSERT(r == 0);

#if 0
r = uv_async_init(&async2_handle, async2_cb, close_cb, NULL);
r = uv_prepare_start(&prepare, prepare_cb);
ASSERT(r == 0);
#endif

r = uv_run(uv_default_loop());
r = uv_async_init(uv_default_loop(), &async, async_cb);
ASSERT(r == 0);

r = uv_wait_thread(thread1_id);
ASSERT(r == 0);
#if 0
r = uv_wait_thread(thread2_id);
ASSERT(r == 0);
r = uv_wait_thread(thread3_id);
r = uv_run(uv_default_loop());
ASSERT(r == 0);
#endif

ASSERT(prepare_cb_called == 2);
ASSERT(async1_cb_called > 2);
/* ASSERT(async2_cb_called = 16); */
ASSERT(prepare_cb_called > 0);
ASSERT(async_cb_called == 3);
ASSERT(close_cb_called == 2);

return 0;
Expand Down

0 comments on commit 5345ee3

Please sign in to comment.