]>
Commit | Line | Data |
---|---|---|
878885b4 TT |
1 | // win32-threads.cc - interface between libjava and Win32 threads. |
2 | ||
4307d0db | 3 | /* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2006 Free Software |
65b8e874 | 4 | Foundation, Inc. |
878885b4 TT |
5 | |
6 | This file is part of libgcj. | |
7 | ||
8 | This software is copyrighted work licensed under the terms of the | |
9 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for | |
10 | details. */ | |
11 | ||
12 | #include <config.h> | |
13 | ||
14 | // If we're using the Boehm GC, then we need to override some of the | |
15 | // thread primitives. This is fairly gross. | |
16 | #ifdef HAVE_BOEHM_GC | |
17 | extern "C" | |
18 | { | |
878885b4 | 19 | #include <gc.h> |
1d3efb36 AM |
20 | // <windows.h> #define's STRICT, which conflicts with Modifier.h |
21 | #undef STRICT | |
878885b4 TT |
22 | }; |
23 | #endif /* HAVE_BOEHM_GC */ | |
24 | ||
25 | #include <gcj/cni.h> | |
26 | #include <jvm.h> | |
27 | #include <java/lang/Thread.h> | |
28 | #include <java/lang/System.h> | |
29 | ||
30 | #include <errno.h> | |
31 | ||
32 | #ifndef ETIMEDOUT | |
33 | #define ETIMEDOUT 116 | |
34 | #endif | |
35 | ||
36 | // This is used to implement thread startup. | |
37 | struct starter | |
38 | { | |
39 | _Jv_ThreadStartFunc *method; | |
878885b4 TT |
40 | _Jv_Thread_t *data; |
41 | }; | |
42 | ||
43 | // Controls access to the variable below | |
44 | static HANDLE daemon_mutex; | |
45 | static HANDLE daemon_cond; | |
46 | // Number of non-daemon threads - _Jv_ThreadWait returns when this is 0 | |
47 | static int non_daemon_count; | |
48 | ||
49 | // TLS key get Java object representing the thread | |
50 | DWORD _Jv_ThreadKey; | |
51 | // TLS key to get _Jv_Thread_t* representing the thread | |
52 | DWORD _Jv_ThreadDataKey; | |
53 | ||
54 | // | |
55 | // These are the flags that can appear in _Jv_Thread_t. | |
56 | // | |
57 | ||
58 | // Thread started. | |
59 | #define FLAG_START 0x01 | |
60 | // Thread is daemon. | |
61 | #define FLAG_DAEMON 0x02 | |
62 | ||
97b8365c TT |
63 | // |
64 | // Helper | |
65 | // | |
66 | inline bool | |
67 | compare_and_exchange(LONG volatile* dest, LONG cmp, LONG xchg) | |
68 | { | |
69 | return InterlockedCompareExchange((LONG*) dest, xchg, cmp) == cmp; | |
70 | // Seems like a bug in the MinGW headers that we have to do this cast. | |
71 | } | |
72 | ||
878885b4 TT |
73 | // |
74 | // Condition variables. | |
75 | // | |
76 | ||
a70cb11b AM |
77 | // we do lazy creation of Events since CreateEvent() is insanely |
78 | // expensive, and because the rest of libgcj will call _Jv_CondInit | |
79 | // when only a mutex is needed. | |
80 | ||
81 | inline void | |
82 | ensure_condvar_initialized(_Jv_ConditionVariable_t *cv) | |
83 | { | |
65b8e874 RM |
84 | if (cv->ev[0] == 0) |
85 | { | |
86 | cv->ev[0] = CreateEvent (NULL, 0, 0, NULL); | |
87 | if (cv->ev[0] == 0) JvFail("CreateEvent() failed"); | |
88 | ||
89 | cv->ev[1] = CreateEvent (NULL, 1, 0, NULL); | |
90 | if (cv->ev[1] == 0) JvFail("CreateEvent() failed"); | |
91 | } | |
a70cb11b AM |
92 | } |
93 | ||
b90e0e3c ME |
94 | inline void |
95 | ensure_interrupt_event_initialized(HANDLE& rhEvent) | |
96 | { | |
97 | if (!rhEvent) | |
98 | { | |
99 | rhEvent = CreateEvent (NULL, 0, 0, NULL); | |
100 | if (!rhEvent) JvFail("CreateEvent() failed"); | |
101 | } | |
102 | } | |
103 | ||
a70cb11b AM |
104 | // Reimplementation of the general algorithm described at |
105 | // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (isomorphic to | |
106 | // 3.2, not a cut-and-paste). | |
107 | ||
878885b4 | 108 | int |
a70cb11b | 109 | _Jv_CondWait(_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos) |
878885b4 | 110 | { |
65b8e874 RM |
111 | if (mu->owner != GetCurrentThreadId ( )) |
112 | return _JV_NOT_OWNER; | |
878885b4 | 113 | |
b90e0e3c ME |
114 | _Jv_Thread_t *current = _Jv_ThreadCurrentData (); |
115 | java::lang::Thread *current_obj = _Jv_ThreadCurrent (); | |
116 | ||
117 | // Now that we hold the interrupt mutex, check if this thread has been | |
118 | // interrupted already. | |
119 | EnterCriticalSection (¤t->interrupt_mutex); | |
120 | ensure_interrupt_event_initialized (current->interrupt_event); | |
121 | jboolean interrupted = current_obj->interrupt_flag; | |
122 | LeaveCriticalSection (¤t->interrupt_mutex); | |
123 | ||
124 | if (interrupted) | |
125 | { | |
126 | return _JV_INTERRUPTED; | |
127 | } | |
128 | ||
65b8e874 RM |
129 | EnterCriticalSection (&cv->count_mutex); |
130 | ensure_condvar_initialized (cv); | |
a70cb11b | 131 | cv->blocked_count++; |
65b8e874 | 132 | LeaveCriticalSection (&cv->count_mutex); |
a70cb11b AM |
133 | |
134 | DWORD time; | |
135 | if ((millis == 0) && (nanos > 0)) time = 1; | |
136 | else if (millis == 0) time = INFINITE; | |
137 | else time = millis; | |
b834f1fa | 138 | |
d6bc9793 ME |
139 | // Record the current lock depth, so it can be restored |
140 | // when we reacquire it. | |
141 | int count = mu->refcount; | |
142 | int curcount = count; | |
143 | ||
144 | // Call _Jv_MutexUnlock repeatedly until this thread | |
145 | // has completely released the monitor. | |
146 | while (curcount > 0) | |
147 | { | |
148 | _Jv_MutexUnlock (mu); | |
149 | --curcount; | |
150 | } | |
878885b4 | 151 | |
b90e0e3c ME |
152 | // Set up our array of three events: |
153 | // - the auto-reset event (for notify()) | |
154 | // - the manual-reset event (for notifyAll()) | |
155 | // - the interrupt event (for interrupt()) | |
156 | // We wait for any one of these to be signaled. | |
157 | HANDLE arh[3]; | |
158 | arh[0] = cv->ev[0]; | |
159 | arh[1] = cv->ev[1]; | |
160 | arh[2] = current->interrupt_event; | |
161 | DWORD rval = WaitForMultipleObjects (3, arh, 0, time); | |
162 | ||
163 | EnterCriticalSection (¤t->interrupt_mutex); | |
164 | ||
165 | // If we were unblocked by the third event (our thread's interrupt | |
166 | // event), set the thread's interrupt flag. I think this sanity | |
167 | // check guards against someone resetting our interrupt flag | |
168 | // in the time between when interrupt_mutex is released in | |
169 | // _Jv_ThreadInterrupt and the interval of time between the | |
170 | // WaitForMultipleObjects call we just made and our acquisition | |
171 | // of interrupt_mutex. | |
172 | if (rval == (WAIT_OBJECT_0 + 2)) | |
173 | current_obj->interrupt_flag = true; | |
174 | ||
175 | interrupted = current_obj->interrupt_flag; | |
176 | LeaveCriticalSection (¤t->interrupt_mutex); | |
a70cb11b AM |
177 | |
178 | EnterCriticalSection(&cv->count_mutex); | |
179 | cv->blocked_count--; | |
65b8e874 RM |
180 | // If we were unblocked by the second event (the broadcast one) |
181 | // and nobody is left, then reset the event. | |
182 | int last_waiter = (rval == (WAIT_OBJECT_0 + 1)) && (cv->blocked_count == 0); | |
a70cb11b AM |
183 | LeaveCriticalSection(&cv->count_mutex); |
184 | ||
65b8e874 RM |
185 | if (last_waiter) |
186 | ResetEvent (cv->ev[1]); | |
878885b4 | 187 | |
d6bc9793 ME |
188 | // Call _Jv_MutexLock repeatedly until the mutex's refcount is the |
189 | // same as before we originally released it. | |
190 | while (curcount < count) | |
191 | { | |
192 | _Jv_MutexLock (mu); | |
193 | ++curcount; | |
194 | } | |
b90e0e3c ME |
195 | |
196 | return interrupted ? _JV_INTERRUPTED : 0; | |
878885b4 TT |
197 | } |
198 | ||
a70cb11b AM |
199 | void |
200 | _Jv_CondInit (_Jv_ConditionVariable_t *cv) | |
201 | { | |
202 | // we do lazy creation of Events since CreateEvent() is insanely expensive | |
203 | cv->ev[0] = 0; | |
65b8e874 | 204 | InitializeCriticalSection (&cv->count_mutex); |
a70cb11b AM |
205 | cv->blocked_count = 0; |
206 | } | |
207 | ||
208 | void | |
209 | _Jv_CondDestroy (_Jv_ConditionVariable_t *cv) | |
210 | { | |
65b8e874 RM |
211 | if (cv->ev[0] != 0) |
212 | { | |
213 | CloseHandle (cv->ev[0]); | |
214 | CloseHandle (cv->ev[1]); | |
215 | ||
216 | cv->ev[0] = 0; | |
217 | } | |
218 | ||
219 | DeleteCriticalSection (&cv->count_mutex); | |
a70cb11b | 220 | } |
878885b4 TT |
221 | |
222 | int | |
65b8e874 | 223 | _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) |
878885b4 | 224 | { |
65b8e874 RM |
225 | if (mu->owner != GetCurrentThreadId ( )) |
226 | return _JV_NOT_OWNER; | |
227 | ||
228 | EnterCriticalSection (&cv->count_mutex); | |
229 | ensure_condvar_initialized (cv); | |
a70cb11b | 230 | int somebody_is_blocked = cv->blocked_count > 0; |
65b8e874 RM |
231 | LeaveCriticalSection (&cv->count_mutex); |
232 | ||
233 | if (somebody_is_blocked) | |
234 | SetEvent (cv->ev[0]); | |
878885b4 | 235 | |
65b8e874 | 236 | return 0; |
a70cb11b | 237 | } |
878885b4 | 238 | |
a70cb11b | 239 | int |
65b8e874 | 240 | _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) |
a70cb11b | 241 | { |
65b8e874 RM |
242 | if (mu->owner != GetCurrentThreadId ( )) |
243 | return _JV_NOT_OWNER; | |
244 | ||
245 | EnterCriticalSection (&cv->count_mutex); | |
246 | ensure_condvar_initialized (cv); | |
a70cb11b | 247 | int somebody_is_blocked = cv->blocked_count > 0; |
65b8e874 | 248 | LeaveCriticalSection (&cv->count_mutex); |
a70cb11b | 249 | |
65b8e874 RM |
250 | if (somebody_is_blocked) |
251 | SetEvent (cv->ev[1]); | |
252 | ||
253 | return 0; | |
878885b4 TT |
254 | } |
255 | ||
256 | // | |
257 | // Threads. | |
258 | // | |
259 | ||
260 | void | |
261 | _Jv_InitThreads (void) | |
262 | { | |
263 | _Jv_ThreadKey = TlsAlloc(); | |
264 | _Jv_ThreadDataKey = TlsAlloc(); | |
65b8e874 RM |
265 | daemon_mutex = CreateMutex (NULL, 0, NULL); |
266 | daemon_cond = CreateEvent (NULL, 1, 0, NULL); | |
878885b4 TT |
267 | non_daemon_count = 0; |
268 | } | |
269 | ||
e301621d | 270 | _Jv_Thread_t * |
c93d7fae | 271 | _Jv_ThreadInitData (java::lang::Thread* obj) |
878885b4 | 272 | { |
2e8f5b2d | 273 | _Jv_Thread_t *data = (_Jv_Thread_t*)_Jv_Malloc(sizeof(_Jv_Thread_t)); |
e301621d | 274 | data->flags = 0; |
a0be84dd | 275 | data->handle = 0; |
c93d7fae | 276 | data->thread_obj = obj; |
b90e0e3c ME |
277 | data->interrupt_event = 0; |
278 | InitializeCriticalSection (&data->interrupt_mutex); | |
878885b4 | 279 | |
e301621d BM |
280 | return data; |
281 | } | |
878885b4 | 282 | |
e301621d BM |
283 | void |
284 | _Jv_ThreadDestroyData (_Jv_Thread_t *data) | |
285 | { | |
b90e0e3c ME |
286 | DeleteCriticalSection (&data->interrupt_mutex); |
287 | if (data->interrupt_event) | |
288 | CloseHandle(data->interrupt_event); | |
a0be84dd | 289 | CloseHandle(data->handle); |
2e8f5b2d | 290 | _Jv_Free(data); |
878885b4 TT |
291 | } |
292 | ||
293 | void | |
294 | _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio) | |
295 | { | |
296 | int actual = THREAD_PRIORITY_NORMAL; | |
297 | ||
298 | if (data->flags & FLAG_START) | |
299 | { | |
300 | switch (prio) | |
301 | { | |
302 | case 10: | |
303 | actual = THREAD_PRIORITY_TIME_CRITICAL; | |
304 | break; | |
305 | case 9: | |
306 | actual = THREAD_PRIORITY_HIGHEST; | |
307 | break; | |
308 | case 8: | |
309 | case 7: | |
310 | actual = THREAD_PRIORITY_ABOVE_NORMAL; | |
311 | break; | |
312 | case 6: | |
313 | case 5: | |
314 | actual = THREAD_PRIORITY_NORMAL; | |
315 | break; | |
316 | case 4: | |
317 | case 3: | |
318 | actual = THREAD_PRIORITY_BELOW_NORMAL; | |
319 | break; | |
320 | case 2: | |
321 | actual = THREAD_PRIORITY_LOWEST; | |
322 | break; | |
323 | case 1: | |
324 | actual = THREAD_PRIORITY_IDLE; | |
325 | break; | |
326 | } | |
327 | SetThreadPriority(data->handle, actual); | |
328 | } | |
329 | } | |
330 | ||
c93d7fae PB |
331 | void |
332 | _Jv_ThreadRegister (_Jv_Thread_t *data) | |
333 | { | |
334 | TlsSetValue (_Jv_ThreadKey, data->thread_obj); | |
335 | TlsSetValue (_Jv_ThreadDataKey, data); | |
336 | } | |
337 | ||
338 | void | |
339 | _Jv_ThreadUnRegister () | |
340 | { | |
341 | TlsSetValue (_Jv_ThreadKey, NULL); | |
342 | TlsSetValue (_Jv_ThreadDataKey, NULL); | |
343 | } | |
344 | ||
878885b4 TT |
345 | // This function is called when a thread is started. We don't arrange |
346 | // to call the `run' method directly, because this function must | |
347 | // return a value. | |
a70cb11b | 348 | static DWORD WINAPI |
878885b4 TT |
349 | really_start (void* x) |
350 | { | |
351 | struct starter *info = (struct starter *) x; | |
352 | ||
c93d7fae PB |
353 | _Jv_ThreadRegister (info->data); |
354 | ||
355 | info->method (info->data->thread_obj); | |
878885b4 TT |
356 | |
357 | if (! (info->data->flags & FLAG_DAEMON)) | |
358 | { | |
359 | WaitForSingleObject (daemon_mutex, INFINITE); | |
360 | non_daemon_count--; | |
361 | if (! non_daemon_count) | |
65b8e874 | 362 | SetEvent (daemon_cond); |
878885b4 TT |
363 | ReleaseMutex (daemon_mutex); |
364 | } | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
369 | void | |
370 | _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, _Jv_ThreadStartFunc *meth) | |
371 | { | |
372 | DWORD id; | |
373 | struct starter *info; | |
374 | ||
375 | // Do nothing if thread has already started | |
376 | if (data->flags & FLAG_START) | |
377 | return; | |
378 | data->flags |= FLAG_START; | |
379 | ||
878885b4 TT |
380 | info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter)); |
381 | info->method = meth; | |
878885b4 TT |
382 | info->data = data; |
383 | ||
384 | if (! thread->isDaemon ()) | |
385 | { | |
386 | WaitForSingleObject (daemon_mutex, INFINITE); | |
387 | non_daemon_count++; | |
388 | ReleaseMutex (daemon_mutex); | |
389 | } | |
390 | else | |
391 | data->flags |= FLAG_DAEMON; | |
392 | ||
a0be84dd | 393 | data->handle = GC_CreateThread(NULL, 0, really_start, info, 0, &id); |
878885b4 | 394 | _Jv_ThreadSetPriority(data, thread->getPriority()); |
878885b4 TT |
395 | } |
396 | ||
397 | void | |
398 | _Jv_ThreadWait (void) | |
399 | { | |
65b8e874 RM |
400 | WaitForSingleObject (daemon_mutex, INFINITE); |
401 | if (non_daemon_count) | |
402 | { | |
403 | ReleaseMutex (daemon_mutex); | |
404 | WaitForSingleObject (daemon_cond, INFINITE); | |
405 | } | |
878885b4 TT |
406 | } |
407 | ||
b90e0e3c ME |
408 | // |
409 | // Interrupt support | |
410 | // | |
411 | ||
412 | HANDLE | |
413 | _Jv_Win32GetInterruptEvent (void) | |
414 | { | |
415 | _Jv_Thread_t *current = _Jv_ThreadCurrentData (); | |
416 | EnterCriticalSection (¤t->interrupt_mutex); | |
417 | ensure_interrupt_event_initialized (current->interrupt_event); | |
418 | HANDLE hEvent = current->interrupt_event; | |
419 | LeaveCriticalSection (¤t->interrupt_mutex); | |
420 | return hEvent; | |
421 | } | |
422 | ||
878885b4 TT |
423 | void |
424 | _Jv_ThreadInterrupt (_Jv_Thread_t *data) | |
425 | { | |
b90e0e3c ME |
426 | EnterCriticalSection (&data->interrupt_mutex); |
427 | ensure_interrupt_event_initialized (data->interrupt_event); | |
428 | data->thread_obj->interrupt_flag = true; | |
429 | SetEvent (data->interrupt_event); | |
430 | LeaveCriticalSection (&data->interrupt_mutex); | |
878885b4 | 431 | } |
4307d0db | 432 | |
97b8365c TT |
433 | // park() / unpark() support |
434 | ||
435 | void | |
436 | ParkHelper::init () | |
437 | { | |
438 | // We initialize our critical section, but not our event. | |
439 | InitializeCriticalSection (&cs); | |
440 | event = NULL; | |
441 | } | |
442 | ||
443 | void | |
444 | ParkHelper::init_event() | |
445 | { | |
446 | EnterCriticalSection (&cs); | |
447 | if (!event) | |
448 | { | |
449 | // Create an auto-reset event. | |
450 | event = CreateEvent(NULL, 0, 0, NULL); | |
451 | if (!event) JvFail("CreateEvent() failed"); | |
452 | } | |
453 | LeaveCriticalSection (&cs); | |
454 | } | |
455 | ||
456 | void | |
457 | ParkHelper::deactivate () | |
458 | { | |
459 | permit = ::java::lang::Thread::THREAD_PARK_DEAD; | |
460 | } | |
461 | ||
462 | void | |
463 | ParkHelper::destroy() | |
464 | { | |
465 | if (event) CloseHandle (event); | |
466 | DeleteCriticalSection (&cs); | |
467 | } | |
468 | ||
469 | /** | |
470 | * Releases the block on a thread created by _Jv_ThreadPark(). This | |
471 | * method can also be used to terminate a blockage caused by a prior | |
472 | * call to park. This operation is unsafe, as the thread must be | |
473 | * guaranteed to be live. | |
474 | * | |
475 | * @param thread the thread to unblock. | |
476 | */ | |
477 | void | |
478 | ParkHelper::unpark () | |
479 | { | |
480 | using namespace ::java::lang; | |
481 | LONG volatile* ptr = &permit; | |
482 | ||
483 | // If this thread is in state RUNNING, give it a permit and return | |
484 | // immediately. | |
485 | if (compare_and_exchange | |
486 | (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PERMIT)) | |
487 | return; | |
488 | ||
489 | // If this thread is parked, put it into state RUNNING and send it a | |
490 | // signal. | |
491 | if (compare_and_exchange | |
492 | (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING)) | |
493 | { | |
494 | init_event (); | |
495 | SetEvent (event); | |
496 | } | |
497 | } | |
498 | ||
499 | /** | |
500 | * Blocks the thread until a matching _Jv_ThreadUnpark() occurs, the | |
501 | * thread is interrupted or the optional timeout expires. If an | |
502 | * unpark call has already occurred, this also counts. A timeout | |
503 | * value of zero is defined as no timeout. When isAbsolute is true, | |
504 | * the timeout is in milliseconds relative to the epoch. Otherwise, | |
505 | * the value is the number of nanoseconds which must occur before | |
506 | * timeout. This call may also return spuriously (i.e. for no | |
507 | * apparent reason). | |
508 | * | |
509 | * @param isAbsolute true if the timeout is specified in milliseconds from | |
510 | * the epoch. | |
511 | * @param time either the number of nanoseconds to wait, or a time in | |
512 | * milliseconds from the epoch to wait for. | |
513 | */ | |
514 | void | |
515 | ParkHelper::park (jboolean isAbsolute, jlong time) | |
516 | { | |
517 | using namespace ::java::lang; | |
518 | LONG volatile* ptr = &permit; | |
519 | ||
520 | // If we have a permit, return immediately. | |
521 | if (compare_and_exchange | |
522 | (ptr, Thread::THREAD_PARK_PERMIT, Thread::THREAD_PARK_RUNNING)) | |
523 | return; | |
524 | ||
525 | // Determine the number of milliseconds to wait. | |
526 | jlong millis = 0, nanos = 0; | |
527 | ||
528 | if (time) | |
529 | { | |
530 | if (isAbsolute) | |
531 | { | |
532 | millis = time - ::java::lang::System::currentTimeMillis(); | |
533 | nanos = 0; | |
534 | } | |
535 | else | |
536 | { | |
537 | millis = 0; | |
538 | nanos = time; | |
539 | } | |
540 | ||
541 | if (nanos) | |
542 | { | |
543 | millis += nanos / 1000000; | |
544 | if (millis == 0) | |
545 | millis = 1; | |
546 | // ...otherwise, we'll block indefinitely. | |
547 | } | |
548 | } | |
549 | ||
550 | if (millis < 0) return; | |
551 | // Can this ever happen? | |
552 | ||
553 | if (compare_and_exchange | |
554 | (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED)) | |
555 | { | |
556 | init_event(); | |
557 | ||
558 | DWORD timeout = millis==0 ? INFINITE : (DWORD) millis; | |
559 | WaitForSingleObject (event, timeout); | |
560 | ||
561 | // If we were unparked by some other thread, this will already | |
562 | // be in state THREAD_PARK_RUNNING. If we timed out, we have to | |
563 | // do it ourself. | |
564 | compare_and_exchange | |
565 | (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING); | |
566 | } | |
567 | } |