Skip to content

Commit

Permalink
corrected pthread_create emulation; no more weird bus errors/crashes …
Browse files Browse the repository at this point in the history
…when mongoose restarts or is idle for a while.
  • Loading branch information
GerHobbelt committed Oct 14, 2012
1 parent e114bfe commit 0c554c6
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 58 deletions.
116 changes: 112 additions & 4 deletions mongoose.c
Original file line number Diff line number Diff line change
Expand Up @@ -2744,6 +2744,100 @@ pthread_t pthread_self(void) {
return GetCurrentThreadId();
}

struct __pthread_thread_func_args {
mg_thread_func_t func;
void *arg;
void *return_value;
unsigned return_value_set: 1;
pthread_t thread_id;
HANDLE thread_h;

struct __pthread_thread_func_args *next;
};

static pthread_spinlock_t __pthread_list_lock;
static struct __pthread_thread_func_args *__pthread_list = NULL;

static unsigned int __stdcall __pthread_starter_func(void *arg) {
struct __pthread_thread_func_args *a = (struct __pthread_thread_func_args *)arg;
DWORD id = GetCurrentThreadId();
void *rv;
struct __pthread_thread_func_args *l, *o;
pthread_spin_lock(&__pthread_list_lock);
l = __pthread_list;
while (l && l->thread_id != id) {
l = l->next;
}
pthread_spin_unlock(&__pthread_list_lock);
assert(l);
assert(l == a);

rv = a->func(a->arg);
if (a->return_value_set)
rv = a->return_value;

pthread_spin_lock(&__pthread_list_lock);
o = NULL;
l = __pthread_list;
while (l && l->thread_id != id) {
l = l->next;
}
if (o && l) {
o->next = l->next;
l->next = NULL;
} else if (l) {
assert(l == __pthread_list);
__pthread_list = l->next;
l->next = NULL;
}
pthread_spin_unlock(&__pthread_list_lock);

_endthreadex((unsigned int)rv);
CloseHandle(l->thread_h);
free(l);
return (unsigned int)rv;
}

int pthread_create(pthread_t * tid, UNUSED_PARAMETER(const pthread_attr_t * attr), mg_thread_func_t start, void *arg) {
struct __pthread_thread_func_args *a = calloc(1, sizeof(*a));
unsigned int t;
uintptr_t rv;
if (!a)
return -1;
a->arg = arg;
a->func = start;
if (!__pthread_list) {
pthread_spin_init(&__pthread_list_lock, PTHREAD_PROCESS_PRIVATE);
}
rv = _beginthreadex(NULL, 0, __pthread_starter_func, a, CREATE_SUSPENDED, &t);
if (rv != 0) {
*tid = t;
a->thread_id = t;
a->thread_h = (HANDLE)rv;
pthread_spin_lock(&__pthread_list_lock);
a->next = __pthread_list;
__pthread_list = a;
pthread_spin_unlock(&__pthread_list_lock);
ResumeThread(a->thread_h);
}
return (rv != 0) ? 0 : errno;
}

void pthread_exit(void *value_ptr) {
DWORD id = GetCurrentThreadId();
struct __pthread_thread_func_args *l;
pthread_spin_lock(&__pthread_list_lock);
l = __pthread_list;
while (l && l->thread_id != id) {
l = l->next;
}
pthread_spin_unlock(&__pthread_list_lock);
assert(l);
if (!l->return_value_set) {
l->return_value = value_ptr;
l->return_value_set = 1;
}
}


// rwlock types have been moved to mongoose_sys_porting.h
Expand Down Expand Up @@ -3106,7 +3200,17 @@ static struct dirent *readdir(DIR *dir) {
#define set_close_on_exec(fd) // No FD_CLOEXEC on Windows

int mg_start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param) {
int rv = _beginthread((void (__cdecl *)(void *)) func, 0, param) == (uintptr_t)-1L ? -1 : 0;
int rv;
pthread_t thread_id;
pthread_attr_t attr;

#if defined(HAVE_PTHREAD)
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
#endif

rv = pthread_create(&thread_id, &attr, func, param);
if (rv == 0) {
// count this thread too so the master_thread will wait for this one to end as well when we stop.
(void) pthread_mutex_lock(&ctx->mutex);
Expand Down Expand Up @@ -4895,7 +4999,7 @@ static void print_dir_entry(struct de *de) {
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
// with __stdcall convention. qsort always requires __cdecl callback.
static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
const char *query_string = a->conn->request_info.query_string;
Expand Down Expand Up @@ -8663,7 +8767,7 @@ static int produce_socket(struct mg_context *ctx, struct mg_connection *conn) {
return rv;
}

static void worker_thread(struct mg_context *ctx) {
static void * WINCDECL worker_thread(struct mg_context *ctx) {
struct mg_connection *conn = NULL;

conn = (struct mg_connection *) malloc(sizeof(*conn) + MAX_REQUEST_SIZE * 2 + CHUNK_HEADER_BUFSIZ); /* RX headers, TX headers, chunk header space */
Expand Down Expand Up @@ -8789,6 +8893,8 @@ static void worker_thread(struct mg_context *ctx) {
// DEBUG_TRACE() or other code while the master thread completes
// due to num_threads reaching zero, which in turn will signal
// mg_stop() to destroy the mutexes, etc..
pthread_exit(0);
return 0;
}

static int accept_new_connection(const struct socket *listener,
Expand Down Expand Up @@ -8847,7 +8953,7 @@ static int accept_new_connection(const struct socket *listener,
}
}

static void master_thread(struct mg_context *ctx) {
static void * WINCDECL master_thread(struct mg_context *ctx) {
fd_set read_set;
struct timeval tv;
struct socket *sp;
Expand Down Expand Up @@ -8979,6 +9085,8 @@ static void master_thread(struct mg_context *ctx) {
// when mg_stop() completes -- and that one destroys all the
// mutexes so writing DEBUG_TRACE() right here would cause a random
// crash due to race conditions.
pthread_exit(0);
return 0;
}

static void free_context(struct mg_context *ctx) {
Expand Down
1 change: 0 additions & 1 deletion mongoose.h
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,6 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,



typedef void * (WINCDECL *mg_thread_func_t)(void *);
// Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error.
int mg_start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param);
Expand Down
17 changes: 15 additions & 2 deletions mongoose_sys_porting.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
#undef WIN32_LEAN_AND_MEAN // Disable WIN32_LEAN_AND_MEAN, if necessary
#endif
#else
#define _XOPEN_SOURCE /*600*/ // For PATH_MAX and flockfile() on Linux
#ifdef __linux__
#define _XOPEN_SOURCE 600 // For PATH_MAX and flockfile() on Linux
#else
#define _XOPEN_SOURCE // BSD
#endif
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#endif
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
Expand Down Expand Up @@ -535,6 +539,7 @@ extern "C" {
typedef HANDLE pthread_mutex_t;
typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
typedef DWORD pthread_t;
typedef struct {WORD dummy;} pthread_attr_t;

struct timespec {
long tv_nsec;
Expand Down Expand Up @@ -753,6 +758,8 @@ typedef int socklen_t;



typedef void * (WINCDECL *mg_thread_func_t)(void *);

#if defined(_WIN32) && !defined(__SYMBIAN32__)

#if !defined(HAVE_PTHREAD)
Expand All @@ -778,6 +785,8 @@ extern "C" {

#undef _POSIX_THREAD_ATTR_STACKSIZE

#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)INVALID_HANDLE_VALUE)

int pthread_mutex_init(pthread_mutex_t *mutex, void *unused);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
Expand All @@ -789,6 +798,8 @@ int pthread_cond_signal(pthread_cond_t *cv);
int pthread_cond_broadcast(pthread_cond_t *cv);
int pthread_cond_destroy(pthread_cond_t *cv);
pthread_t pthread_self(void);
int pthread_create(pthread_t * tid, const pthread_attr_t * attr, mg_thread_func_t start, void *arg);
void pthread_exit(void *value_ptr);

#if !defined(USE_SRWLOCK)
#if defined(RTL_SRWLOCK_INIT) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
Expand Down Expand Up @@ -822,10 +833,12 @@ int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

//#define PTHREAD_SPINLOCK_INITIALIZER PTHREAD_MUTEX_INITIALIZER
#define PTHREAD_SPINLOCK_INITIALIZER PTHREAD_MUTEX_INITIALIZER

typedef pthread_mutex_t pthread_spinlock_t;

#define PTHREAD_PROCESS_PRIVATE 0

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_lock(pthread_spinlock_t *lock);
Expand Down
50 changes: 0 additions & 50 deletions test/unit_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1672,47 +1672,6 @@ static void test_client_connect() {


// now with HTTP header support:
ASSERT_STREQ(get_option(ctx, MAX_REQUEST_SIZE), "");
conn = mg_connect(ctx, "www.google.com", 80, MG_CONNECT_BASIC | MG_CONNECT_HTTP_IO);
ASSERT(!conn);

// all options are empty
ASSERT_STREQ(get_option(ctx, MAX_REQUEST_SIZE), "");
// so we should set them up, just like one would've got when calling mg_start():
ctx->config[MAX_REQUEST_SIZE] = "256";

conn = mg_connect(ctx, "www.google.com", 80, MG_CONNECT_BASIC | MG_CONNECT_HTTP_IO);
ASSERT(conn);

ASSERT(0 == mg_add_tx_header(conn, 0, "Host", "www.google.com"));
ASSERT(0 == mg_add_tx_header(conn, 0, "Connection", "close"));
// set up the request the rude way: directly patch the request_info struct. Nasty!
//
// Setting us up cf. https://developers.google.com/custom-search/docs/xml_results?hl=en#WebSearch_Request_Format
ri4m = (struct mg_request_info *)mg_get_request_info(conn);
ri4m->http_version = "1.1";
ri4m->query_string = "q=mongoose&num=5&client=google-csbe&ie=utf8&oe=utf8&cx=00255077836266642015:u-scht7a-8i";
ri4m->request_method = "GET";
ri4m->uri = "/search";

rv = mg_write_http_request_head(conn, NULL, NULL);
ASSERT(rv == 153);
// signal request phase done:
mg_shutdown(conn, SHUT_WR);
// fetch response, blocking I/O:
//
// but since this is a HTTP I/O savvy connection, we should first read the headers and parse them:
rv = mg_read_http_response_head(conn);
// google will spit back more than 256-1 header bytes in its response, so we'll get a buffer overrun:
ASSERT(rv == 413);
ASSERT(conn->request_len == 0);
mg_close_connection(conn);



// retry with a suitably large buffer:
ctx->config[MAX_REQUEST_SIZE] = "2048";

conn = mg_connect(ctx, "www.google.com", 80, MG_CONNECT_BASIC | MG_CONNECT_HTTP_IO);
ASSERT(conn);

Expand Down Expand Up @@ -2096,14 +2055,6 @@ static void test_local_client_connect() {


// now with HTTP header support:
ASSERT_STREQ(get_option(&ctx_client, MAX_REQUEST_SIZE), "");
ASSERT(get_option(ctx, MAX_REQUEST_SIZE));
ASSERT(atoi(get_option(ctx, MAX_REQUEST_SIZE)) > 0);

ctx_client.config[MAX_REQUEST_SIZE] = mg_strdup(get_option(ctx, MAX_REQUEST_SIZE));
ASSERT(get_option(&ctx_client, MAX_REQUEST_SIZE));
ASSERT(atoi(get_option(&ctx_client, MAX_REQUEST_SIZE)) > 0);

conn = mg_connect(&ctx_client, "localhost", 33797, MG_CONNECT_HTTP_IO);
ASSERT(conn);

Expand Down Expand Up @@ -2146,7 +2097,6 @@ static void test_local_client_connect() {
// cleanup
mg_stop(ctx);
free(ctx_client.config[MAX_REQUEST_SIZE]);
}


Expand Down

0 comments on commit 0c554c6

Please sign in to comment.