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:
parent
3908af563c
commit
0eca049a9b
@ -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)
|
||||
|
||||
18
include/uv.h
18
include/uv.h
@ -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);
|
||||
|
||||
@ -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, ¶ms, 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;
|
||||
|
||||
@ -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, ¶ms, 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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user