Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

corrected pthread_create emulation; no more weird bus errors/crashes …

…when mongoose restarts or is idle for a while.
  • Loading branch information...
commit 0c554c6cd799708856594ea7b4f12f48bd109bc1 1 parent e114bfe
@GerHobbelt authored
View
116 mongoose.c
@@ -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
@@ -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);
@@ -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;
@@ -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 */
@@ -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,
@@ -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;
@@ -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) {
View
1  mongoose.h
@@ -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);
View
17 mongoose_sys_porting.h
@@ -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++
@@ -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;
@@ -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)
@@ -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);
@@ -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)
@@ -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);
2  pthread-win32
@@ -1 +1 @@
-Subproject commit c93f4778e0b7600894053e034e455ae5abc0510a
+Subproject commit 2740214d883d95cf919dc37d8cb45cfb1c2e91a2
View
50 test/unit_test.c
@@ -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);
@@ -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);
@@ -2146,7 +2097,6 @@ static void test_local_client_connect() {
// cleanup
mg_stop(ctx);
- free(ctx_client.config[MAX_REQUEST_SIZE]);
}
Please sign in to comment.
Something went wrong with that request. Please try again.