thread: allow specifying stack size for new thread

PR-URL: https://github.com/libuv/libuv/pull/2179
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
Anna Henningsen 2019-02-06 14:48:33 +01:00
parent 3908af563c
commit 0eca049a9b
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
6 changed files with 153 additions and 5 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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)
@ -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,

View File

@ -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)
@ -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)

View File

@ -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;
@ -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;
@ -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
}
@ -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;
}