~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to thread_win32.c

  • Committer: Bazaar Package Importer
  • Author(s): Stephan Hermann
  • Date: 2008-01-24 11:42:29 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20080124114229-jw2f87rdxlq6gp11
Tags: 1.9.0.0-2ubuntu1
* Merge from debian unstable, remaining changes:
  - Robustify check for target_os, fixing build failure on lpia.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*-c-*- */
 
2
/**********************************************************************
 
3
 
 
4
  thread_win32.c -
 
5
 
 
6
  $Author: ko1 $
 
7
  $Date: 2007-12-25 13:35:17 +0900 (Tue, 25 Dec 2007) $
 
8
 
 
9
  Copyright (C) 2004-2007 Koichi Sasada
 
10
 
 
11
**********************************************************************/
 
12
 
 
13
#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
 
14
 
 
15
#include <process.h>
 
16
 
 
17
#define WIN32_WAIT_TIMEOUT 10   /* 10 ms */
 
18
#undef Sleep
 
19
 
 
20
#define native_thread_yield() Sleep(0)
 
21
#define remove_signal_thread_list(th)
 
22
 
 
23
static volatile DWORD ruby_native_thread_key = TLS_OUT_OF_INDEXES;
 
24
 
 
25
static int native_mutex_lock(rb_thread_lock_t *);
 
26
static int native_mutex_unlock(rb_thread_lock_t *);
 
27
static int native_mutex_trylock(rb_thread_lock_t *);
 
28
static void native_mutex_initialize(rb_thread_lock_t *);
 
29
 
 
30
static void native_cond_signal(rb_thread_cond_t *cond);
 
31
static void native_cond_broadcast(rb_thread_cond_t *cond);
 
32
static void native_cond_wait(rb_thread_cond_t *cond, rb_thread_lock_t *mutex);
 
33
static void native_cond_initialize(rb_thread_cond_t *cond);
 
34
static void native_cond_destroy(rb_thread_cond_t *cond);
 
35
 
 
36
static rb_thread_t *
 
37
ruby_thread_from_native(void)
 
38
{
 
39
    return TlsGetValue(ruby_native_thread_key);
 
40
}
 
41
 
 
42
static int
 
43
ruby_thread_set_native(rb_thread_t *th)
 
44
{
 
45
    return TlsSetValue(ruby_native_thread_key, th);
 
46
}
 
47
 
 
48
static void
 
49
Init_native_thread(void)
 
50
{
 
51
    rb_thread_t *th = GET_THREAD();
 
52
 
 
53
    ruby_native_thread_key = TlsAlloc();
 
54
    DuplicateHandle(GetCurrentProcess(),
 
55
                    GetCurrentThread(),
 
56
                    GetCurrentProcess(),
 
57
                    &th->thread_id, 0, FALSE, DUPLICATE_SAME_ACCESS);
 
58
 
 
59
    th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
 
60
 
 
61
    thread_debug("initial thread (th: %p, thid: %p, event: %p)\n",
 
62
                 th, GET_THREAD()->thread_id,
 
63
                 th->native_thread_data.interrupt_event);
 
64
}
 
65
 
 
66
static void
 
67
w32_error(void)
 
68
{
 
69
    LPVOID lpMsgBuf;
 
70
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
 
71
                  FORMAT_MESSAGE_FROM_SYSTEM |
 
72
                  FORMAT_MESSAGE_IGNORE_INSERTS,
 
73
                  NULL,
 
74
                  GetLastError(),
 
75
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 
76
                  (LPTSTR) & lpMsgBuf, 0, NULL);
 
77
    rb_bug("%s", lpMsgBuf);
 
78
}
 
79
 
 
80
static void
 
81
w32_set_event(HANDLE handle)
 
82
{
 
83
    if (SetEvent(handle) == 0) {
 
84
        w32_error();
 
85
    }
 
86
}
 
87
 
 
88
static void
 
89
w32_reset_event(HANDLE handle)
 
90
{
 
91
    if (ResetEvent(handle) == 0) {
 
92
        w32_error();
 
93
    }
 
94
}
 
95
 
 
96
static int
 
97
w32_wait_events(HANDLE *events, int count, DWORD timeout, rb_thread_t *th)
 
98
{
 
99
    HANDLE *targets = events;
 
100
    HANDLE intr;
 
101
    DWORD ret;
 
102
 
 
103
    thread_debug("  w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n",
 
104
                 events, count, timeout, th);
 
105
    if (th && (intr = th->native_thread_data.interrupt_event)) {
 
106
        w32_reset_event(intr);
 
107
        if (th->interrupt_flag) {
 
108
            w32_set_event(intr);
 
109
        }
 
110
 
 
111
        targets = ALLOCA_N(HANDLE, count + 1);
 
112
        memcpy(targets, events, sizeof(HANDLE) * count);
 
113
 
 
114
        targets[count++] = intr;
 
115
        thread_debug("  * handle: %p (count: %d, intr)\n", intr, count);
 
116
    }
 
117
 
 
118
    thread_debug("  WaitForMultipleObjects start (count: %d)\n", count);
 
119
    ret = WaitForMultipleObjects(count, targets, FALSE, timeout);
 
120
    thread_debug("  WaitForMultipleObjects end (ret: %d)\n", ret);
 
121
 
 
122
    if (ret == WAIT_OBJECT_0 + count - 1 && th) {
 
123
        errno = EINTR;
 
124
    }
 
125
    if (ret == -1 && THREAD_DEBUG) {
 
126
        int i;
 
127
        DWORD dmy;
 
128
        for (i = 0; i < count; i++) {
 
129
            thread_debug("  * error handle %d - %s\n", i,
 
130
                         GetHandleInformation(targets[i], &dmy) ? "OK" : "NG");
 
131
        }
 
132
    }
 
133
    return ret;
 
134
}
 
135
 
 
136
static void ubf_handle(void *ptr);
 
137
#define ubf_select ubf_handle
 
138
 
 
139
int
 
140
rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD timeout)
 
141
{
 
142
    return w32_wait_events(events, num, timeout, GET_THREAD());
 
143
}
 
144
 
 
145
int
 
146
rb_w32_wait_events(HANDLE *events, int num, DWORD timeout)
 
147
{
 
148
    int ret;
 
149
 
 
150
    BLOCKING_REGION(ret = rb_w32_wait_events_blocking(events, num, timeout),
 
151
                    ubf_handle, GET_THREAD());
 
152
    return ret;
 
153
}
 
154
 
 
155
static void
 
156
w32_close_handle(HANDLE handle)
 
157
{
 
158
    if (CloseHandle(handle) == 0) {
 
159
        w32_error();
 
160
    }
 
161
}
 
162
 
 
163
static void
 
164
w32_resume_thread(HANDLE handle)
 
165
{
 
166
    if (ResumeThread(handle) == -1) {
 
167
        w32_error();
 
168
    }
 
169
}
 
170
 
 
171
#ifdef _MSC_VER
 
172
#define HAVE__BEGINTHREADEX 1
 
173
#else
 
174
#undef HAVE__BEGINTHREADEX
 
175
#endif
 
176
 
 
177
#ifdef HAVE__BEGINTHREADEX
 
178
#define start_thread (HANDLE)_beginthreadex
 
179
typedef unsigned long (_stdcall *w32_thread_start_func)(void*);
 
180
#else
 
181
#define start_thread CreateThread
 
182
typedef LPTHREAD_START_ROUTINE w32_thread_start_func;
 
183
#endif
 
184
 
 
185
static HANDLE
 
186
w32_create_thread(DWORD stack_size, w32_thread_start_func func, void *val)
 
187
{
 
188
    return start_thread(0, stack_size, func, val, CREATE_SUSPENDED, 0);
 
189
}
 
190
 
 
191
int
 
192
rb_w32_sleep(unsigned long msec)
 
193
{
 
194
    return w32_wait_events(0, 0, msec, GET_THREAD());
 
195
}
 
196
 
 
197
int WINAPI
 
198
rb_w32_Sleep(unsigned long msec)
 
199
{
 
200
    int ret;
 
201
 
 
202
    BLOCKING_REGION(ret = rb_w32_sleep(msec),
 
203
                    ubf_handle, GET_THREAD());
 
204
    return ret;
 
205
}
 
206
 
 
207
static void
 
208
native_sleep(rb_thread_t *th, struct timeval *tv)
 
209
{
 
210
    DWORD msec;
 
211
    if (tv) {
 
212
        msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
 
213
    }
 
214
    else {
 
215
        msec = INFINITE;
 
216
    }
 
217
 
 
218
    GVL_UNLOCK_BEGIN();
 
219
    {
 
220
        DWORD ret;
 
221
        int status = th->status;
 
222
 
 
223
        th->status = THREAD_STOPPED;
 
224
        th->unblock_function = ubf_handle;
 
225
        th->unblock_function_arg = th;
 
226
 
 
227
        if (RUBY_VM_INTERRUPTED(th)) {
 
228
            /* interrupted.  return immediate */
 
229
        }
 
230
        else {
 
231
            thread_debug("native_sleep start (%d)\n", (int)msec);
 
232
            ret = w32_wait_events(0, 0, msec, th);
 
233
            thread_debug("native_sleep done (%d)\n", ret);
 
234
        }
 
235
 
 
236
        th->unblock_function = 0;
 
237
        th->unblock_function_arg = 0;
 
238
        th->status = status;
 
239
    }
 
240
    GVL_UNLOCK_END();
 
241
    RUBY_VM_CHECK_INTS();
 
242
}
 
243
 
 
244
static int
 
245
native_mutex_lock(rb_thread_lock_t *lock)
 
246
{
 
247
#if USE_WIN32_MUTEX
 
248
    DWORD result;
 
249
    while (1) {
 
250
        thread_debug("native_mutex_lock: %p\n", *lock);
 
251
        result = w32_wait_events(&*lock, 1, INFINITE, 0);
 
252
        switch (result) {
 
253
          case WAIT_OBJECT_0:
 
254
            /* get mutex object */
 
255
            thread_debug("acquire mutex: %p\n", *lock);
 
256
            return 0;
 
257
          case WAIT_OBJECT_0 + 1:
 
258
            /* interrupt */
 
259
            errno = EINTR;
 
260
            thread_debug("acquire mutex interrupted: %p\n", *lock);
 
261
            return 0;
 
262
          case WAIT_TIMEOUT:
 
263
            thread_debug("timeout mutex: %p\n", *lock);
 
264
            break;
 
265
          case WAIT_ABANDONED:
 
266
            rb_bug("win32_mutex_lock: WAIT_ABANDONED");
 
267
            break;
 
268
          default:
 
269
            rb_bug("win32_mutex_lock: unknown result (%d)", result);
 
270
            break;
 
271
        }
 
272
    }
 
273
    return 0;
 
274
#else
 
275
    EnterCriticalSection(lock);
 
276
    return 0;
 
277
#endif
 
278
}
 
279
 
 
280
static int
 
281
native_mutex_unlock(rb_thread_lock_t *lock)
 
282
{
 
283
#if USE_WIN32_MUTEX
 
284
    thread_debug("release mutex: %p\n", *lock);
 
285
    return ReleaseMutex(*lock);
 
286
#else
 
287
    LeaveCriticalSection(lock);
 
288
    return 0;
 
289
#endif
 
290
}
 
291
 
 
292
static int
 
293
native_mutex_trylock(rb_thread_lock_t *lock)
 
294
{
 
295
#if USE_WIN32_MUTEX
 
296
    int result;
 
297
    thread_debug("native_mutex_trylock: %p\n", *lock);
 
298
    result = w32_wait_events(&*lock, 1, 1, 0);
 
299
    thread_debug("native_mutex_trylock result: %d\n", result);
 
300
    switch (result) {
 
301
      case WAIT_OBJECT_0:
 
302
        return 0;
 
303
      case WAIT_TIMEOUT:
 
304
        return EBUSY;
 
305
    }
 
306
    return EINVAL;
 
307
#else
 
308
    return TryEnterCriticalSection(lock) == 0;
 
309
#endif
 
310
}
 
311
 
 
312
static void
 
313
native_mutex_initialize(rb_thread_lock_t *lock)
 
314
{
 
315
#if USE_WIN32_MUTEX
 
316
    *lock = CreateMutex(NULL, FALSE, NULL);
 
317
    if (*lock == NULL) {
 
318
        w32_error();
 
319
    }
 
320
    /* thread_debug("initialize mutex: %p\n", *lock); */
 
321
#else
 
322
    InitializeCriticalSection(lock);
 
323
#endif
 
324
}
 
325
 
 
326
static void
 
327
native_mutex_destroy(rb_thread_lock_t *lock)
 
328
{
 
329
#if USE_WIN32_MUTEX
 
330
    w32_close_handle(lock);
 
331
#else
 
332
    DeleteCriticalSection(lock);
 
333
#endif
 
334
}
 
335
 
 
336
struct cond_event_entry {
 
337
    struct cond_event_entry* next;
 
338
    HANDLE event;
 
339
};
 
340
 
 
341
struct rb_thread_cond_struct {
 
342
    struct cond_event_entry *next;
 
343
    struct cond_event_entry *last;
 
344
};
 
345
 
 
346
static void
 
347
native_cond_signal(rb_thread_cond_t *cond)
 
348
{
 
349
    /* cond is guarded by mutex */
 
350
    struct cond_event_entry *e = cond->next;
 
351
 
 
352
    if (e) {
 
353
        cond->next = e->next;
 
354
        SetEvent(e->event);
 
355
    }
 
356
    else {
 
357
        rb_bug("native_cond_signal: no pending threads");
 
358
    }
 
359
}
 
360
 
 
361
static void
 
362
native_cond_broadcast(rb_thread_cond_t *cond)
 
363
{
 
364
    /* cond is guarded by mutex */
 
365
    struct cond_event_entry *e = cond->next;
 
366
    cond->next = 0;
 
367
 
 
368
    while (e) {
 
369
        SetEvent(e->event);
 
370
        e = e->next;
 
371
    }
 
372
}
 
373
 
 
374
static void
 
375
native_cond_wait(rb_thread_cond_t *cond, rb_thread_lock_t *mutex)
 
376
{
 
377
    DWORD r;
 
378
    struct cond_event_entry entry;
 
379
 
 
380
    entry.next = 0;
 
381
    entry.event = CreateEvent(0, FALSE, FALSE, 0);
 
382
 
 
383
    /* cond is guarded by mutex */
 
384
    if (cond->next) {
 
385
        cond->last->next = &entry;
 
386
        cond->last = &entry;
 
387
    }
 
388
    else {
 
389
        cond->next = &entry;
 
390
        cond->last = &entry;
 
391
    }
 
392
 
 
393
    native_mutex_unlock(mutex);
 
394
    {
 
395
        r = WaitForSingleObject(entry.event, INFINITE);
 
396
        if (r != WAIT_OBJECT_0) {
 
397
            rb_bug("native_cond_wait: WaitForSingleObject returns %d", r);
 
398
        }
 
399
    }
 
400
    native_mutex_lock(mutex);
 
401
 
 
402
    w32_close_handle(entry.event);
 
403
}
 
404
 
 
405
static void
 
406
native_cond_initialize(rb_thread_cond_t *cond)
 
407
{
 
408
    cond->next = 0;
 
409
    cond->last = 0;
 
410
}
 
411
 
 
412
static void
 
413
native_cond_destroy(rb_thread_cond_t *cond)
 
414
{
 
415
    /* */
 
416
}
 
417
 
 
418
static void
 
419
native_thread_destroy(rb_thread_t *th)
 
420
{
 
421
    HANDLE intr = th->native_thread_data.interrupt_event;
 
422
    thread_debug("close handle - intr: %p, thid: %p\n", intr, th->thread_id);
 
423
    th->native_thread_data.interrupt_event = 0;
 
424
    w32_close_handle(intr);
 
425
}
 
426
 
 
427
static unsigned long _stdcall
 
428
thread_start_func_1(void *th_ptr)
 
429
{
 
430
    rb_thread_t *th = th_ptr;
 
431
    VALUE stack_start;
 
432
    volatile HANDLE thread_id = th->thread_id;
 
433
 
 
434
    th->native_thread_data.interrupt_event = CreateEvent(0, TRUE, FALSE, 0);
 
435
 
 
436
    /* run */
 
437
    thread_debug("thread created (th: %p, thid: %p, event: %p)\n", th,
 
438
                 th->thread_id, th->native_thread_data.interrupt_event);
 
439
    thread_start_func_2(th, &stack_start, 0);
 
440
 
 
441
    w32_close_handle(thread_id);
 
442
    thread_debug("thread deleted (th: %p)\n", th);
 
443
    return 0;
 
444
}
 
445
 
 
446
extern size_t rb_gc_stack_maxsize;
 
447
 
 
448
static int
 
449
native_thread_create(rb_thread_t *th)
 
450
{
 
451
    size_t stack_size = 4 * 1024; /* 4KB */
 
452
    th->thread_id = w32_create_thread(stack_size, thread_start_func_1, th);
 
453
 
 
454
    th->machine_stack_maxsize = rb_gc_stack_maxsize; /* not tested. */
 
455
 
 
456
    if ((th->thread_id) == 0) {
 
457
        st_delete_wrap(th->vm->living_threads, th->self);
 
458
        rb_raise(rb_eThreadError, "can't create Thread (%d)", errno);
 
459
    }
 
460
 
 
461
    w32_resume_thread(th->thread_id);
 
462
 
 
463
    if (THREAD_DEBUG) {
 
464
        Sleep(0);
 
465
        thread_debug("create: (th: %p, thid: %p, intr: %p), stack size: %d\n",
 
466
                     th, th->thread_id,
 
467
                     th->native_thread_data.interrupt_event, stack_size);
 
468
    }
 
469
    return 0;
 
470
}
 
471
 
 
472
static void
 
473
native_thread_join(HANDLE th)
 
474
{
 
475
    w32_wait_events(&th, 1, 0, 0);
 
476
}
 
477
 
 
478
static void
 
479
native_thread_apply_priority(rb_thread_t *th)
 
480
{
 
481
    int priority = th->priority;
 
482
    if (th->priority > 0) {
 
483
        priority = THREAD_PRIORITY_ABOVE_NORMAL;
 
484
    }
 
485
    else if (th->priority < 0) {
 
486
        priority = THREAD_PRIORITY_BELOW_NORMAL;
 
487
    }
 
488
    else {
 
489
        priority = THREAD_PRIORITY_NORMAL;
 
490
    }
 
491
 
 
492
    SetThreadPriority(th->thread_id, priority);
 
493
}
 
494
 
 
495
static void
 
496
ubf_handle(void *ptr)
 
497
{
 
498
    rb_thread_t *th = (rb_thread_t *)ptr;
 
499
    thread_debug("ubf_handle: %p\n", th);
 
500
    w32_set_event(th->native_thread_data.interrupt_event);
 
501
}
 
502
 
 
503
static void timer_thread_function(void);
 
504
 
 
505
static HANDLE timer_thread_id = 0;
 
506
 
 
507
static unsigned long _stdcall
 
508
timer_thread_func(void *dummy)
 
509
{
 
510
    thread_debug("timer_thread\n");
 
511
    while (system_working) {
 
512
        Sleep(WIN32_WAIT_TIMEOUT);
 
513
        timer_thread_function();
 
514
    }
 
515
    thread_debug("timer killed\n");
 
516
    return 0;
 
517
}
 
518
 
 
519
void
 
520
rb_thread_create_timer_thread(void)
 
521
{
 
522
    if (timer_thread_id == 0) {
 
523
        timer_thread_id = w32_create_thread(1024, timer_thread_func, 0);
 
524
        w32_resume_thread(timer_thread_id);
 
525
    }
 
526
}
 
527
 
 
528
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */