Skip to content

Commit 2d69323

Browse files
addaleaxnjlr
authored andcommitted
thread: allow specifying stack size for new thread
PR-URL: libuv#2179 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
1 parent 281d633 commit 2d69323

File tree

6 files changed

+153
-5
lines changed

6 files changed

+153
-5
lines changed

docs/src/threading.rst

+29
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,39 @@ API
5555
Threads
5656
^^^^^^^
5757

58+
.. c:type:: uv_thread_options_t
59+
60+
Options for spawning a new thread (passed to :c:func:`uv_thread_create_ex`).
61+
62+
::
63+
64+
typedef struct uv_process_options_s {
65+
enum {
66+
UV_THREAD_NO_FLAGS = 0x00,
67+
UV_THREAD_HAS_STACK_SIZE = 0x01
68+
} flags;
69+
size_t stack_size;
70+
} uv_process_options_t;
71+
72+
More fields may be added to this struct at any time, so its exact
73+
layout and size should not be relied upon.
74+
75+
.. versionadded:: 1.26.0
76+
5877
.. c:function:: int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg)
5978
6079
.. versionchanged:: 1.4.1 returns a UV_E* error code on failure
6180
81+
.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg)
82+
83+
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
84+
85+
If `UV_THREAD_HAS_STACK_SIZE` is set, `stack_size` specifies a stack size for the new thread.
86+
`0` indicates that the default value should be used, i.e. behaves as if the flag was not set.
87+
Other values will be rounded up to the nearest page boundary.
88+
89+
.. versionadded:: 1.26.0
90+
6291
.. c:function:: uv_thread_t uv_thread_self(void)
6392
.. c:function:: int uv_thread_join(uv_thread_t *tid)
6493
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)

include/uv.h

+18
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,24 @@ UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
15851585
typedef void (*uv_thread_cb)(void* arg);
15861586

15871587
UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
1588+
1589+
typedef enum {
1590+
UV_THREAD_NO_FLAGS = 0x00,
1591+
UV_THREAD_HAS_STACK_SIZE = 0x01
1592+
} uv_thread_create_flags;
1593+
1594+
struct uv_thread_options_s {
1595+
unsigned int flags;
1596+
size_t stack_size;
1597+
/* More fields may be added at any time. */
1598+
};
1599+
1600+
typedef struct uv_thread_options_s uv_thread_options_t;
1601+
1602+
UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
1603+
const uv_thread_options_t* params,
1604+
uv_thread_cb entry,
1605+
void* arg);
15881606
UV_EXTERN uv_thread_t uv_thread_self(void);
15891607
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
15901608
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);

src/unix/thread.c

+23-2
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,34 @@ static size_t thread_stack_size(void) {
194194

195195

196196
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
197+
uv_thread_options_t params;
198+
params.flags = UV_THREAD_NO_FLAGS;
199+
return uv_thread_create_ex(tid, &params, entry, arg);
200+
}
201+
202+
int uv_thread_create_ex(uv_thread_t* tid,
203+
const uv_thread_options_t* params,
204+
void (*entry)(void *arg),
205+
void *arg) {
197206
int err;
198-
size_t stack_size;
199207
pthread_attr_t* attr;
200208
pthread_attr_t attr_storage;
209+
size_t pagesize;
210+
size_t stack_size;
211+
212+
stack_size =
213+
params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
201214

202215
attr = NULL;
203-
stack_size = thread_stack_size();
216+
if (stack_size == 0) {
217+
stack_size = thread_stack_size();
218+
} else {
219+
pagesize = (size_t)getpagesize();
220+
/* Round up to the nearest page boundary. */
221+
stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
222+
if (stack_size < PTHREAD_STACK_MIN)
223+
stack_size = PTHREAD_STACK_MIN;
224+
}
204225

205226
if (stack_size > 0) {
206227
attr = &attr_storage;

src/win/thread.c

+26-1
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,34 @@ static UINT __stdcall uv__thread_start(void* arg) {
112112

113113

114114
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
115+
uv_thread_options_t params;
116+
params.flags = UV_THREAD_NO_FLAGS;
117+
return uv_thread_create_ex(tid, &params, entry, arg);
118+
}
119+
120+
int uv_thread_create_ex(uv_thread_t* tid,
121+
const uv_thread_options_t* params,
122+
void (*entry)(void *arg),
123+
void *arg) {
115124
struct thread_ctx* ctx;
116125
int err;
117126
HANDLE thread;
127+
SYSTEM_INFO sysinfo;
128+
size_t stack_size;
129+
size_t pagesize;
130+
131+
stack_size =
132+
params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
133+
134+
if (stack_size != 0) {
135+
GetNativeSystemInfo(&sysinfo);
136+
pagesize = (size_t)sysinfo.dwPageSize;
137+
/* Round up to the nearest page boundary. */
138+
stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
139+
140+
if ((unsigned)stack_size != stack_size)
141+
return UV_EINVAL;
142+
}
118143

119144
ctx = uv__malloc(sizeof(*ctx));
120145
if (ctx == NULL)
@@ -126,7 +151,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
126151
/* Create the thread in suspended state so we have a chance to pass
127152
* its own creation handle to it */
128153
thread = (HANDLE) _beginthreadex(NULL,
129-
0,
154+
(unsigned)stack_size,
130155
uv__thread_start,
131156
ctx,
132157
CREATE_SUSPENDED,

test/test-list.h

+2
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ TEST_DECLARE (threadpool_cancel_fs)
365365
TEST_DECLARE (threadpool_cancel_single)
366366
TEST_DECLARE (thread_local_storage)
367367
TEST_DECLARE (thread_stack_size)
368+
TEST_DECLARE (thread_stack_size_explicit)
368369
TEST_DECLARE (thread_mutex)
369370
TEST_DECLARE (thread_mutex_recursive)
370371
TEST_DECLARE (thread_rwlock)
@@ -930,6 +931,7 @@ TASK_LIST_START
930931
TEST_ENTRY (threadpool_cancel_single)
931932
TEST_ENTRY (thread_local_storage)
932933
TEST_ENTRY (thread_stack_size)
934+
TEST_ENTRY (thread_stack_size_explicit)
933935
TEST_ENTRY (thread_mutex)
934936
TEST_ENTRY (thread_mutex_recursive)
935937
TEST_ENTRY (thread_rwlock)

test/test-thread.c

+55-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
#include <stdlib.h>
2727
#include <string.h> /* memset */
2828

29+
#ifdef __POSIX__
30+
#include <pthread.h>
31+
#endif
32+
2933
struct getaddrinfo_req {
3034
uv_thread_t thread_id;
3135
unsigned int counter;
@@ -207,10 +211,15 @@ TEST_IMPL(thread_local_storage) {
207211

208212
static void thread_check_stack(void* arg) {
209213
#if defined(__APPLE__)
214+
size_t expected;
215+
expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
210216
/* 512 kB is the default stack size of threads other than the main thread
211217
* on MacOS. */
212-
ASSERT(pthread_get_stacksize_np(pthread_self()) > 512*1024);
218+
if (expected == 0)
219+
expected = 512 * 1024;
220+
ASSERT(pthread_get_stacksize_np(pthread_self()) >= expected);
213221
#elif defined(__linux__) && defined(__GLIBC__)
222+
size_t expected;
214223
struct rlimit lim;
215224
size_t stack_size;
216225
pthread_attr_t attr;
@@ -219,7 +228,10 @@ static void thread_check_stack(void* arg) {
219228
lim.rlim_cur = 2 << 20; /* glibc default. */
220229
ASSERT(0 == pthread_getattr_np(pthread_self(), &attr));
221230
ASSERT(0 == pthread_attr_getstacksize(&attr, &stack_size));
222-
ASSERT(stack_size >= lim.rlim_cur);
231+
expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
232+
if (expected == 0)
233+
expected = (size_t)lim.rlim_cur;
234+
ASSERT(stack_size >= expected);
223235
#endif
224236
}
225237

@@ -230,3 +242,44 @@ TEST_IMPL(thread_stack_size) {
230242
ASSERT(0 == uv_thread_join(&thread));
231243
return 0;
232244
}
245+
246+
TEST_IMPL(thread_stack_size_explicit) {
247+
uv_thread_t thread;
248+
uv_thread_options_t options;
249+
250+
options.flags = UV_THREAD_HAS_STACK_SIZE;
251+
options.stack_size = 1024 * 1024;
252+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
253+
thread_check_stack, &options));
254+
ASSERT(0 == uv_thread_join(&thread));
255+
256+
options.stack_size = 8 * 1024 * 1024; /* larger than most default os sizes */
257+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
258+
thread_check_stack, &options));
259+
ASSERT(0 == uv_thread_join(&thread));
260+
261+
options.stack_size = 0;
262+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
263+
thread_check_stack, &options));
264+
ASSERT(0 == uv_thread_join(&thread));
265+
266+
#ifdef PTHREAD_STACK_MIN
267+
options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */
268+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
269+
thread_check_stack, &options));
270+
ASSERT(0 == uv_thread_join(&thread));
271+
272+
options.stack_size = PTHREAD_STACK_MIN / 2 - 42; /* unaligned size */
273+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
274+
thread_check_stack, &options));
275+
ASSERT(0 == uv_thread_join(&thread));
276+
#endif
277+
278+
/* unaligned size, should be larger than PTHREAD_STACK_MIN */
279+
options.stack_size = 1234567;
280+
ASSERT(0 == uv_thread_create_ex(&thread, &options,
281+
thread_check_stack, &options));
282+
ASSERT(0 == uv_thread_join(&thread));
283+
284+
return 0;
285+
}

0 commit comments

Comments
 (0)