4
* Copyright Ericsson AB 2010. All Rights Reserved.
6
* The contents of this file are subject to the Erlang Public License,
7
* Version 1.1, (the "License"); you may not use this file except in
8
* compliance with the License. You should have received a copy of the
9
* Erlang Public License along with this software. If not, it can be
10
* retrieved online at http://www.erlang.org/.
12
* Software distributed under the License is distributed on an "AS IS"
13
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
* the License for the specific language governing rights and limitations
21
* Description: Windows native threads implementation of the ethread library
22
* Author: Rickard Green
29
#define ETHR_CHILD_WAIT_SPIN_COUNT 4000
31
#undef WIN32_LEAN_AND_MEAN
32
#define WIN32_LEAN_AND_MEAN
39
#define ETHR_INLINE_FUNC_NAME_(X) X ## __
40
#define ETHREAD_IMPL__
43
#include "ethr_internal.h"
45
#ifndef ETHR_HAVE_ETHREAD_DEFINES
46
#error Missing configure defines
49
/* Argument passed to thr_wrapper() */
52
ethr_atomic32_t result;
54
void *(*thr_func)(void *);
57
} ethr_thr_wrap_data__;
59
#define ETHR_INVALID_TID_ID -1
61
struct ethr_join_data_ {
66
static ethr_atomic_t thread_id_counter;
67
static DWORD own_tid_key;
68
static ethr_tid main_thr_tid;
69
static int child_wait_spin_count;
71
DWORD ethr_ts_event_key__;
73
#define ETHR_GET_OWN_TID__ ((ethr_tid *) TlsGetValue(own_tid_key))
76
* --------------------------------------------------------------------------
78
* --------------------------------------------------------------------------
81
static void thr_exit_cleanup(ethr_tid *tid, void *res)
84
ETHR_ASSERT(tid == ETHR_GET_OWN_TID__);
87
tid->jdata->res = res;
89
ethr_run_exit_handlers__();
90
ethr_ts_event_destructor__((void *) ethr_get_tse__());
93
static unsigned __stdcall thr_wrapper(LPVOID vtwd)
98
ethr_thr_wrap_data__ *twd = (ethr_thr_wrap_data__ *) vtwd;
99
void *(*thr_func)(void *) = twd->thr_func;
100
void *arg = twd->arg;
101
ethr_ts_event *tsep = NULL;
103
result = (ethr_sint32_t) ethr_make_ts_event__(&tsep);
106
tsep->iflgs |= ETHR_TS_EV_ETHREAD;
108
if (!TlsSetValue(own_tid_key, (LPVOID) &my_tid)) {
109
result = (ethr_sint32_t) ethr_win_get_errno__();
110
ethr_free_ts_event__(tsep);
113
if (ethr_thr_child_func__)
114
ethr_thr_child_func__(twd->prep_func_res);
118
tsep = twd->tse; /* We aren't allowed to follow twd after
119
result has been set! */
121
ethr_atomic32_set(&twd->result, result);
123
ethr_event_set(&tsep->event);
125
res = result == 0 ? (*thr_func)(arg) : NULL;
127
thr_exit_cleanup(&my_tid, res);
131
/* internal exports */
134
ethr_win_get_errno__(void)
136
return erts_get_last_win_errno();
139
int ethr_set_tse__(ethr_ts_event *tsep)
141
return (TlsSetValue(ethr_ts_event_key__, (LPVOID) tsep)
143
: ethr_win_get_errno__());
146
ethr_ts_event *ethr_get_tse__(void)
148
return (ethr_ts_event *) TlsGetValue(ethr_ts_event_key__);
162
* ----------------------------------------------------------------------------
164
* ----------------------------------------------------------------------------
168
ethr_init(ethr_init_data *id)
171
DWORD major = (_WIN32_WINNT >> 8) & 0xff;
172
DWORD minor = _WIN32_WINNT & 0xff;
173
OSVERSIONINFO os_version;
178
if (!ethr_not_inited__)
182
os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
183
GetVersionEx(&os_version);
184
if (os_version.dwPlatformId != VER_PLATFORM_WIN32_NT
185
|| os_version.dwMajorVersion < major
186
|| (os_version.dwMajorVersion == major
187
&& os_version.dwMinorVersion < minor))
190
err = ethr_init_common__(id);
194
own_tid_key = TlsAlloc();
195
if (own_tid_key == TLS_OUT_OF_INDEXES)
198
ethr_atomic_init(&thread_id_counter, 0);
201
main_thr_tid.jdata = NULL;
203
if (!TlsSetValue(own_tid_key, (LPVOID) &main_thr_tid))
206
ETHR_ASSERT(&main_thr_tid == ETHR_GET_OWN_TID__);
208
ethr_ts_event_key__ = TlsAlloc();
209
if (ethr_ts_event_key__ == TLS_OUT_OF_INDEXES)
212
child_wait_spin_count = ETHR_CHILD_WAIT_SPIN_COUNT;
213
if (erts_get_cpu_configured(ethr_cpu_info__) == 1)
214
child_wait_spin_count = 0;
216
ethr_not_inited__ = 0;
221
ethr_not_inited__ = 1;
223
err = ethr_win_get_errno__();
224
ETHR_ASSERT(err != 0);
229
ethr_late_init(ethr_late_init_data *id)
231
int res = ethr_late_init_common__(id);
234
ethr_not_completely_inited__ = 0;
244
ethr_thr_create(ethr_tid *tid, void * (*func)(void *), void *arg,
247
HANDLE handle = INVALID_HANDLE_VALUE;
249
ethr_thr_wrap_data__ twd;
252
unsigned stack_size = 0; /* 0 = system default */
253
int use_stack_size = (opts && opts->suggested_stack_size >= 0
254
? opts->suggested_stack_size
255
: -1 /* Use system default */);
257
#ifdef ETHR_MODIFIED_DEFAULT_STACK_SIZE
258
if (use_stack_size < 0)
259
use_stack_size = ETHR_MODIFIED_DEFAULT_STACK_SIZE;
263
if (ethr_not_completely_inited__) {
274
tid->id = ethr_atomic_inc_read(&thread_id_counter);
275
} while (tid->id == ETHR_INVALID_TID_ID);
277
if (opts && opts->detached)
280
tid->jdata = ethr_mem__.std.alloc(sizeof(struct ethr_join_data_));
283
tid->jdata->handle = INVALID_HANDLE_VALUE;
284
tid->jdata->res = NULL;
287
if (use_stack_size >= 0) {
288
size_t suggested_stack_size = (size_t) use_stack_size;
290
suggested_stack_size /= 2; /* Make sure we got margin */
292
if (suggested_stack_size < ethr_min_stack_size__)
293
stack_size = (unsigned) ETHR_KW2B(ethr_min_stack_size__);
294
else if (suggested_stack_size > ethr_max_stack_size__)
295
stack_size = (unsigned) ETHR_KW2B(ethr_max_stack_size__);
297
stack_size = (unsigned)
298
ETHR_PAGE_ALIGN(ETHR_KW2B(suggested_stack_size));
301
ethr_atomic32_init(&twd.result, -1);
306
twd.tse = ethr_get_ts_event();
308
/* Call prepare func if it exist */
309
if (ethr_thr_prepare_func__)
310
twd.prep_func_res = ethr_thr_prepare_func__();
312
twd.prep_func_res = NULL;
314
/* spawn the thr_wrapper function */
315
handle = (HANDLE) _beginthreadex(NULL, stack_size, thr_wrapper,
316
(LPVOID) &twd, 0, &ID);
317
if (handle == (HANDLE) 0) {
318
handle = INVALID_HANDLE_VALUE;
322
int spin_count = child_wait_spin_count;
324
ETHR_ASSERT(handle != INVALID_HANDLE_VALUE);
329
tid->jdata->handle = handle;
331
/* Wait for child to initialize... */
333
ethr_sint32_t result;
335
ethr_event_reset(&twd.tse->event);
337
result = ethr_atomic32_read(&twd.result);
346
err = ethr_event_swait(&twd.tse->event, spin_count);
347
if (err && err != EINTR)
353
if (ethr_thr_parent_func__)
354
ethr_thr_parent_func__(twd.prep_func_res);
357
ethr_leave_ts_event(twd.tse);
364
err = ethr_win_get_errno__();
365
ETHR_ASSERT(err != 0);
367
if (ethr_thr_parent_func__)
368
ethr_thr_parent_func__(twd.prep_func_res);
370
if (handle != INVALID_HANDLE_VALUE) {
371
WaitForSingleObject(handle, INFINITE);
376
ethr_mem__.std.free(tid->jdata);
380
tid->id = ETHR_INVALID_TID_ID;
383
ethr_leave_ts_event(twd.tse);
388
int ethr_thr_join(ethr_tid tid, void **res)
393
if (ethr_not_inited__) {
399
if (tid.id == ETHR_INVALID_TID_ID || !tid.jdata)
402
/* Wait for thread to terminate */
403
code = WaitForSingleObject(tid.jdata->handle, INFINITE);
404
if (code != WAIT_OBJECT_0)
405
return ethr_win_get_errno__();
407
CloseHandle(tid.jdata->handle);
408
tid.jdata->handle = INVALID_HANDLE_VALUE;
411
*res = tid.jdata->res;
414
* User better not try to join or detach again; or
415
* bad things will happen... (users responsibility)
418
ethr_mem__.std.free(tid.jdata);
425
ethr_thr_detach(ethr_tid tid)
428
if (ethr_not_inited__) {
434
if (tid.id == ETHR_INVALID_TID_ID || !tid.jdata)
437
CloseHandle(tid.jdata->handle);
438
tid.jdata->handle = INVALID_HANDLE_VALUE;
441
* User better not try to join or detach again; or
442
* bad things will happen... (users responsibility)
445
ethr_mem__.std.free(tid.jdata);
452
ethr_thr_exit(void *res)
456
if (ethr_not_inited__) {
461
tid = ETHR_GET_OWN_TID__;
464
_endthreadex((unsigned) 0);
466
thr_exit_cleanup(tid, res);
467
_endthreadex((unsigned) 0);
475
if (ethr_not_inited__) {
476
ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, NULL};
481
/* It is okay for non-ethreads (i.e. native win32 threads) to call
482
ethr_self(). They will however be returned an invalid tid. */
483
tid = ETHR_GET_OWN_TID__;
485
ethr_tid dummy_tid = {ETHR_INVALID_TID_ID, NULL};
492
ethr_equal_tids(ethr_tid tid1, ethr_tid tid2)
494
/* An invalid tid does not equal any tid, not even an invalid tid */
495
return tid1.id == tid2.id && tid1.id != ETHR_INVALID_TID_ID;
499
* Thread specific data
503
ethr_tsd_key_create(ethr_tsd_key *keyp)
507
if (ethr_not_inited__) {
517
if (key == TLS_OUT_OF_INDEXES)
518
return ethr_win_get_errno__();
519
*keyp = (ethr_tsd_key) key;
524
ethr_tsd_key_delete(ethr_tsd_key key)
527
if (ethr_not_inited__) {
532
if (!TlsFree((DWORD) key))
533
return ethr_win_get_errno__();
538
ethr_tsd_set(ethr_tsd_key key, void *value)
541
if (ethr_not_inited__) {
546
if (!TlsSetValue((DWORD) key, (LPVOID) value))
547
return ethr_win_get_errno__();
552
ethr_tsd_get(ethr_tsd_key key)
555
if (ethr_not_inited__) {
560
return (void *) TlsGetValue((DWORD) key);
565
* Thread specific events
569
ethr_get_ts_event(void)
571
return ethr_get_ts_event__();
575
ethr_leave_ts_event(ethr_ts_event *tsep)
577
ethr_leave_ts_event__(tsep);
581
ethr_create_ts_event__(void)
584
ethr_make_ts_event__(&tsep);