Skip to content

Commit

Permalink
thread: allow specifying stack size for new thread
Browse files Browse the repository at this point in the history
PR-URL: libuv#2179
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
  • Loading branch information
addaleax committed Feb 8, 2019
1 parent 3908af5 commit 0eca049
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 5 deletions.
29 changes: 29 additions & 0 deletions docs/src/threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,39 @@ API
Threads
^^^^^^^

.. c:type:: uv_thread_options_t
Options for spawning a new thread (passed to :c:func:`uv_thread_create_ex`).

::

typedef struct uv_process_options_s {
enum {
UV_THREAD_NO_FLAGS = 0x00,
UV_THREAD_HAS_STACK_SIZE = 0x01
} flags;
size_t stack_size;
} uv_process_options_t;

More fields may be added to this struct at any time, so its exact
layout and size should not be relied upon.

.. versionadded:: 1.26.0

.. c:function:: int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg)
.. versionchanged:: 1.4.1 returns a UV_E* error code on failure
.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg)
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
If `UV_THREAD_HAS_STACK_SIZE` is set, `stack_size` specifies a stack size for the new thread.
`0` indicates that the default value should be used, i.e. behaves as if the flag was not set.
Other values will be rounded up to the nearest page boundary.
.. versionadded:: 1.26.0
.. c:function:: uv_thread_t uv_thread_self(void)
.. c:function:: int uv_thread_join(uv_thread_t *tid)
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
Expand Down
18 changes: 18 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,24 @@ UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
typedef void (*uv_thread_cb)(void* arg);

UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);

typedef enum {
UV_THREAD_NO_FLAGS = 0x00,
UV_THREAD_HAS_STACK_SIZE = 0x01
} uv_thread_create_flags;

struct uv_thread_options_s {
unsigned int flags;
size_t stack_size;
/* More fields may be added at any time. */
};

typedef struct uv_thread_options_s uv_thread_options_t;

UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
const uv_thread_options_t* params,
uv_thread_cb entry,
void* arg);
UV_EXTERN uv_thread_t uv_thread_self(void);
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
Expand Down
25 changes: 23 additions & 2 deletions src/unix/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,34 @@ static size_t thread_stack_size(void) {


int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
uv_thread_options_t params;
params.flags = UV_THREAD_NO_FLAGS;
return uv_thread_create_ex(tid, &params, entry, arg);
}

int uv_thread_create_ex(uv_thread_t* tid,
const uv_thread_options_t* params,
void (*entry)(void *arg),
void *arg) {
int err;
size_t stack_size;
pthread_attr_t* attr;
pthread_attr_t attr_storage;
size_t pagesize;
size_t stack_size;

stack_size =
params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;

attr = NULL;
stack_size = thread_stack_size();
if (stack_size == 0) {
stack_size = thread_stack_size();
} else {
pagesize = (size_t)getpagesize();
/* Round up to the nearest page boundary. */
stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
if (stack_size < PTHREAD_STACK_MIN)
stack_size = PTHREAD_STACK_MIN;
}

if (stack_size > 0) {
attr = &attr_storage;
Expand Down
27 changes: 26 additions & 1 deletion src/win/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,34 @@ static UINT __stdcall uv__thread_start(void* arg) {


int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
uv_thread_options_t params;
params.flags = UV_THREAD_NO_FLAGS;
return uv_thread_create_ex(tid, &params, entry, arg);
}

int uv_thread_create_ex(uv_thread_t* tid,
const uv_thread_options_t* params,
void (*entry)(void *arg),
void *arg) {
struct thread_ctx* ctx;
int err;
HANDLE thread;
SYSTEM_INFO sysinfo;
size_t stack_size;
size_t pagesize;

stack_size =
params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;

if (stack_size != 0) {
GetNativeSystemInfo(&sysinfo);
pagesize = (size_t)sysinfo.dwPageSize;
/* Round up to the nearest page boundary. */
stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);

if ((unsigned)stack_size != stack_size)
return UV_EINVAL;
}

ctx = uv__malloc(sizeof(*ctx));
if (ctx == NULL)
Expand All @@ -126,7 +151,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
/* Create the thread in suspended state so we have a chance to pass
* its own creation handle to it */
thread = (HANDLE) _beginthreadex(NULL,
0,
(unsigned)stack_size,
uv__thread_start,
ctx,
CREATE_SUSPENDED,
Expand Down
2 changes: 2 additions & 0 deletions test/test-list.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ TEST_DECLARE (threadpool_cancel_fs)
TEST_DECLARE (threadpool_cancel_single)
TEST_DECLARE (thread_local_storage)
TEST_DECLARE (thread_stack_size)
TEST_DECLARE (thread_stack_size_explicit)
TEST_DECLARE (thread_mutex)
TEST_DECLARE (thread_mutex_recursive)
TEST_DECLARE (thread_rwlock)
Expand Down Expand Up @@ -930,6 +931,7 @@ TASK_LIST_START
TEST_ENTRY (threadpool_cancel_single)
TEST_ENTRY (thread_local_storage)
TEST_ENTRY (thread_stack_size)
TEST_ENTRY (thread_stack_size_explicit)
TEST_ENTRY (thread_mutex)
TEST_ENTRY (thread_mutex_recursive)
TEST_ENTRY (thread_rwlock)
Expand Down
57 changes: 55 additions & 2 deletions test/test-thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
#include <stdlib.h>
#include <string.h> /* memset */

#ifdef __POSIX__
#include <pthread.h>
#endif

struct getaddrinfo_req {
uv_thread_t thread_id;
unsigned int counter;
Expand Down Expand Up @@ -207,10 +211,15 @@ TEST_IMPL(thread_local_storage) {

static void thread_check_stack(void* arg) {
#if defined(__APPLE__)
size_t expected;
expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
/* 512 kB is the default stack size of threads other than the main thread
* on MacOS. */
ASSERT(pthread_get_stacksize_np(pthread_self()) > 512*1024);
if (expected == 0)
expected = 512 * 1024;
ASSERT(pthread_get_stacksize_np(pthread_self()) >= expected);
#elif defined(__linux__) && defined(__GLIBC__)
size_t expected;
struct rlimit lim;
size_t stack_size;
pthread_attr_t attr;
Expand All @@ -219,7 +228,10 @@ static void thread_check_stack(void* arg) {
lim.rlim_cur = 2 << 20; /* glibc default. */
ASSERT(0 == pthread_getattr_np(pthread_self(), &attr));
ASSERT(0 == pthread_attr_getstacksize(&attr, &stack_size));
ASSERT(stack_size >= lim.rlim_cur);
expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
if (expected == 0)
expected = (size_t)lim.rlim_cur;
ASSERT(stack_size >= expected);
#endif
}

Expand All @@ -230,3 +242,44 @@ TEST_IMPL(thread_stack_size) {
ASSERT(0 == uv_thread_join(&thread));
return 0;
}

TEST_IMPL(thread_stack_size_explicit) {
uv_thread_t thread;
uv_thread_options_t options;

options.flags = UV_THREAD_HAS_STACK_SIZE;
options.stack_size = 1024 * 1024;
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));

options.stack_size = 8 * 1024 * 1024; /* larger than most default os sizes */
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));

options.stack_size = 0;
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));

#ifdef PTHREAD_STACK_MIN
options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));

options.stack_size = PTHREAD_STACK_MIN / 2 - 42; /* unaligned size */
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));
#endif

/* unaligned size, should be larger than PTHREAD_STACK_MIN */
options.stack_size = 1234567;
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));

return 0;
}

0 comments on commit 0eca049

Please sign in to comment.