stop world
[cacao.git] / threads / nativethread.c
1 #include "global.h"
2
3 #if defined(NATIVE_THREADS)
4
5 #include <stdlib.h>
6 #include <string.h>
7 #include <assert.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <sys/time.h>
12 #include <time.h>
13 #include <errno.h>
14
15 #include "config.h"
16 #include "thread.h"
17 #include "locks.h"
18 #include "tables.h"
19 #include "native.h"
20 #include "loader.h"
21 #include "builtin.h"
22 #include "asmpart.h"
23 #include "toolbox/loging.h"
24 #include "toolbox/memory.h"
25 #include "toolbox/avl.h"
26 #include "mm/boehm.h"
27
28 #include "nat/java_lang_Object.h"
29 #include "nat/java_lang_Throwable.h"
30 #include "nat/java_lang_Thread.h"
31 #include "nat/java_lang_ThreadGroup.h"
32
33 #include <pthread.h>
34 #include <semaphore.h>
35
36 #if defined(__LINUX__)
37 #define GC_LINUX_THREADS
38 #include "../mm/boehm-gc/include/gc.h"
39 #endif
40
41 #include "machine-instr.h"
42
43 static struct avl_table *criticaltree;
44 static threadobject *mainthreadobj;
45
46 #ifndef HAVE___THREAD
47 pthread_key_t tkey_threadinfo;
48 #else
49 __thread threadobject *threadobj;
50 #endif
51
52 static pthread_mutex_t cast_mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
53 static pthread_mutex_t compiler_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
54 static pthread_mutex_t tablelock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
55
56 void cast_lock()
57 {
58         pthread_mutex_lock(&cast_mutex);
59 }
60
61 void cast_unlock()
62 {
63         pthread_mutex_unlock(&cast_mutex);
64 }
65
66 void compiler_lock()
67 {
68         pthread_mutex_lock(&compiler_mutex);
69 }
70
71 void compiler_unlock()
72 {
73         pthread_mutex_unlock(&compiler_mutex);
74 }
75
76 void tables_lock()
77 {
78     pthread_mutex_lock(&tablelock);
79 }
80
81 void tables_unlock()
82 {
83     pthread_mutex_unlock(&tablelock);
84 }
85
86 static int criticalcompare(const void *pa, const void *pb, void *param)
87 {
88         const threadcritnode *na = pa;
89         const threadcritnode *nb = pb;
90
91         if (na->mcodebegin < nb->mcodebegin)
92                 return -1;
93         if (na->mcodebegin > nb->mcodebegin)
94                 return 1;
95         return 0;
96 }
97
98 static const threadcritnode *findcritical(u1 *mcodeptr)
99 {
100     struct avl_node *n = criticaltree->avl_root;
101     const threadcritnode *m = NULL;
102     if (!n)
103         return NULL;
104     for (;;)
105     {
106         const threadcritnode *d = n->avl_data;
107         if (mcodeptr == d->mcodebegin)
108             return d;
109         if (mcodeptr < d->mcodebegin) {
110             if (n->avl_link[0])
111                 n = n->avl_link[0];
112             else
113                 return m;
114         } else {
115             if (n->avl_link[1]) {
116                 m = n->avl_data;
117                 n = n->avl_link[1];
118             } else
119                 return n->avl_data;
120         }
121     }
122 }
123
124 void thread_registercritical(threadcritnode *n)
125 {
126         avl_insert(criticaltree, n);
127 }
128
129 static u1 *thread_checkcritical(u1 *mcodeptr)
130 {
131         const threadcritnode *n = findcritical(mcodeptr);
132         return (n && mcodeptr < n->mcodeend) ? n->mcodebegin : NULL;
133 }
134
135 static pthread_mutex_t threadlistlock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
136
137 static pthread_mutex_t stopworldlock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
138 volatile int stopworldwhere;
139
140 static sem_t suspend_ack;
141
142 /*
143  * where - 1 from within GC
144            2 class numbering
145  */
146 void lock_stopworld(int where)
147 {
148         pthread_mutex_lock(&stopworldlock);
149         stopworldwhere = where;
150 }
151
152 void unlock_stopworld()
153 {
154         stopworldwhere = 0;
155         pthread_mutex_unlock(&stopworldlock);
156 }
157
158 /* Caller must hold threadlistlock */
159 static int cast_sendsignals(int sig, int count)
160 {
161         /* Count threads */
162         threadobject *tobj = mainthreadobj;
163         nativethread *infoself = THREADINFO;
164
165         if (count == 0)
166                 do {
167                         count++;
168                         tobj = tobj->info.next;
169                 } while (tobj != mainthreadobj);
170
171         do {
172                 nativethread *info = &tobj->info;
173                 if (info != infoself)
174                         pthread_kill(info->tid, sig);
175                 tobj = tobj->info.next;
176         } while (tobj != mainthreadobj);
177
178         return count-1;
179 }
180
181 void cast_stopworld()
182 {
183         int count, i;
184         lock_stopworld(2);
185         pthread_mutex_lock(&threadlistlock);
186         count = cast_sendsignals(GC_signum1(), 0);
187         for (i=0; i<count; i++)
188                 sem_wait(&suspend_ack);
189         pthread_mutex_unlock(&threadlistlock);
190 }
191
192 void cast_startworld()
193 {
194         pthread_mutex_lock(&threadlistlock);
195         cast_sendsignals(GC_signum2(), -1);
196         pthread_mutex_unlock(&threadlistlock);
197         unlock_stopworld();
198 }
199
200 static void sigsuspend_handler(struct sigcontext *ctx)
201 {
202         int sig;
203         sigset_t sigs;
204         void *critical;
205         
206 #ifdef __I386__
207         if ((critical = thread_checkcritical((void*) ctx->eip)) != NULL)
208                 ctx->eip = (long) critical;
209 #endif
210
211         sem_post(&suspend_ack);
212
213         sig = GC_signum2();
214         sigfillset(&sigs);
215         sigdelset(&sigs, sig);
216         sigsuspend(&sigs);
217 }
218
219 int cacao_suspendhandler(struct sigcontext *ctx)
220 {
221         if (stopworldwhere != 2)
222                 return 0;
223
224         sigsuspend_handler(ctx);
225         return 1;
226 }
227
228 static void setthreadobject(threadobject *thread)
229 {
230 #if !defined(HAVE___THREAD)
231         pthread_setspecific(tkey_threadinfo, thread);
232 #else
233         threadobj = thread;
234 #endif
235 }
236
237 /*
238  * Initialize threads.
239  */
240 void
241 initThreadsEarly()
242 {
243         struct sigaction sa;
244
245         /* Allocate something so the garbage collector's signal handlers are  */
246         /* installed. */
247         heap_allocate(1, false, NULL);
248
249         mainthreadobj = NEW(threadobject);
250         memset(mainthreadobj, 0, sizeof(threadobject));
251 #if !defined(HAVE___THREAD)
252         pthread_key_create(&tkey_threadinfo, NULL);
253 #endif
254         setthreadobject(mainthreadobj);
255
256     criticaltree = avl_create(criticalcompare, NULL, NULL);
257         sem_init(&suspend_ack, 0, 0);
258 }
259
260 static void freeLockRecordPools(lockRecordPool *);
261
262 void
263 initThreads(u1 *stackbottom)
264 {
265         classinfo *threadclass;
266         java_lang_Thread *mainthread;
267         threadobject *tempthread = mainthreadobj;
268
269         threadclass = loader_load_sysclass(NULL,utf_new_char("java/lang/Thread"));
270         assert(threadclass);
271         freeLockRecordPools(mainthreadobj->ee.lrpool);
272         threadclass->instancesize = sizeof(threadobject);
273         mainthreadobj = (threadobject*) builtin_new(threadclass);
274         assert(mainthreadobj);
275         FREE(tempthread, threadobject);
276         initThread(&mainthreadobj->o);
277
278 #if !defined(HAVE___THREAD)
279         pthread_setspecific(tkey_threadinfo, mainthreadobj);
280 #else
281         threadobj = mainthreadobj;
282 #endif
283
284         mainthread = &mainthreadobj->o;
285         initLocks();
286         mainthreadobj->info.next = mainthreadobj;
287         mainthreadobj->info.prev = mainthreadobj;
288
289         mainthread->name=javastring_new(utf_new_char("main"));
290
291         /* Allocate and init ThreadGroup */
292         mainthread->group = (java_lang_ThreadGroup*)native_new_and_init(loader_load(utf_new_char("java/lang/ThreadGroup")));
293         assert(mainthread->group != 0);
294 }
295
296 void initThread(java_lang_Thread *t)
297 {
298         nativethread *info = &((threadobject*) t)->info;
299         pthread_mutex_init(&info->joinMutex, NULL);
300         pthread_cond_init(&info->joinCond, NULL);
301 }
302
303 static void initThreadLocks(threadobject *);
304
305 static void *threadstartup(void *t)
306 {
307         threadobject *thread = t;
308         nativethread *info = &thread->info;
309         threadobject *tnext;
310         methodinfo *method;
311
312         setthreadobject(thread);
313
314         pthread_mutex_lock(&threadlistlock);
315         info->prev = mainthreadobj;
316         info->next = tnext = mainthreadobj->info.next;
317         mainthreadobj->info.next = t;
318         tnext->info.prev = t;
319         pthread_mutex_unlock(&threadlistlock);
320
321         initThreadLocks(thread);
322
323         /* Find the run()V method and call it */
324         method = class_findmethod(thread->o.header.vftbl->class,
325                                                           utf_new_char("run"), utf_new_char("()V"));
326         if (method == 0)
327                 panic("Cannot find method \'void run ()\'");
328
329         asm_calljavafunction(method, thread, NULL, NULL, NULL);
330
331     if (info->_exceptionptr) {
332         utf_display((info->_exceptionptr)->vftbl->class->name);
333         printf("\n");
334     }
335
336         freeLockRecordPools(thread->ee.lrpool);
337
338         pthread_mutex_lock(&threadlistlock);
339         info->next->info.prev = info->prev;
340         info->prev->info.next = info->next;
341         pthread_mutex_unlock(&threadlistlock);
342
343         pthread_mutex_lock(&info->joinMutex);
344         info->tid = 0;
345         pthread_mutex_unlock(&info->joinMutex);
346         pthread_cond_broadcast(&info->joinCond);
347
348         return NULL;
349 }
350
351 void startThread(threadobject *t)
352 {
353         nativethread *info = &t->info;
354         pthread_create(&info->tid, NULL, threadstartup, t);
355         pthread_detach(info->tid);
356 }
357
358 void joinAllThreads()
359 {
360         pthread_mutex_lock(&threadlistlock);
361         while (mainthreadobj->info.prev != mainthreadobj) {
362                 nativethread *info = &mainthreadobj->info.prev->info;
363                 pthread_mutex_lock(&info->joinMutex);
364                 pthread_mutex_unlock(&threadlistlock);
365                 if (info->tid)
366                         pthread_cond_wait(&info->joinCond, &info->joinMutex);
367                 pthread_mutex_unlock(&info->joinMutex);
368                 pthread_mutex_lock(&threadlistlock);
369         }
370         pthread_mutex_unlock(&threadlistlock);
371 }
372
373 bool aliveThread(java_lang_Thread *t)
374 {
375         return ((threadobject*) t)->info.tid != 0;
376 }
377
378 void sleepThread(s8 millis)
379 {
380         struct timespec tv;
381         tv.tv_sec = millis / 1000;
382         tv.tv_nsec = millis % 1000 * 1000000;
383         do { } while (nanosleep(&tv, &tv) == EINTR);
384 }
385
386 void yieldThread()
387 {
388         sched_yield();
389 }
390
391 static void timedCondWait(pthread_cond_t *cond, pthread_mutex_t *mutex, s8 millis)
392 {
393         struct timeval now;
394         struct timespec desttime;
395         gettimeofday(&now, NULL);
396         desttime.tv_sec = millis / 1000;
397         desttime.tv_nsec = millis % 1000 * 1000000;
398         pthread_cond_timedwait(cond, mutex, &desttime);
399 }
400
401
402 #define NEUTRAL 0
403 #define LOCKED 1
404 #define WAITERS 2
405 #define BUSY 3
406
407 static void initExecutionEnvironment(ExecEnvironment *ee)
408 {
409         pthread_mutex_init(&ee->metaLockMutex, NULL);
410         pthread_cond_init(&ee->metaLockCond, NULL);
411         pthread_mutex_init(&ee->monitorLockMutex, NULL);
412         pthread_cond_init(&ee->monitorLockCond, NULL);
413 }
414
415 static void initLockRecord(monitorLockRecord *r, threadobject *t)
416 {
417         r->owner = t;
418         r->lockCount = 1;
419         r->queue = NULL;
420 }
421
422 void initLocks()
423 {
424         initThreadLocks(mainthreadobj);
425 }
426
427 static void initThreadLocks(threadobject *thread)
428 {
429         int i;
430
431         initExecutionEnvironment(&thread->ee);
432         for (i=0; i<INITIALLOCKRECORDS; i++) {
433                 monitorLockRecord *r = &thread->ee.lr[i];
434                 initLockRecord(r, thread);
435                 r->nextFree = &thread->ee.lr[i+1];
436         }
437         thread->ee.lr[i-1].nextFree = NULL;
438         thread->ee.firstLR = &thread->ee.lr[0];
439 }
440
441 static inline int lockState(long r)
442 {
443         return r & 3;
444 }
445
446 static inline void *lockRecord(long r)
447 {
448         return (void*) (r & ~3);
449 }
450
451 static inline long makeLockBits(void *r, long l)
452 {
453         return ((long) r) | l;
454 }
455
456 static lockRecordPool *allocLockRecordPool(threadobject *thread, int size)
457 {
458         lockRecordPool *p = mem_alloc(sizeof(lockRecordPoolHeader) + sizeof(monitorLockRecord) * size);
459         int i;
460
461         p->header.size = size;
462         for (i=0; i<size; i++) {
463                 initLockRecord(&p->lr[i], thread);
464                 p->lr[i].nextFree = &p->lr[i+1];
465         }
466         p->lr[i-1].nextFree = NULL;
467         return p;
468 }
469
470 static void freeLockRecordPools(lockRecordPool *pool)
471 {
472         while (pool) {
473                 lockRecordPool *n = pool->header.next;
474                 mem_free(pool, sizeof(lockRecordPoolHeader) + sizeof(monitorLockRecord) * pool->header.size);
475                 pool = n;
476         }
477 }
478
479 static monitorLockRecord *allocLockRecord(threadobject *t)
480 {
481         monitorLockRecord *r = t->ee.firstLR;
482
483         if (!r) {
484                 int poolsize = t->ee.lrpool ? t->ee.lrpool->header.size * 2 : INITIALLOCKRECORDS * 2;
485                 lockRecordPool *pool = allocLockRecordPool(t, poolsize);
486                 pool->header.next = t->ee.lrpool;
487                 t->ee.lrpool = pool;
488                 r = &pool->lr[0];
489         }
490         
491         t->ee.firstLR = r->nextFree;
492         return r;
493 }
494
495 static void recycleLockRecord(threadobject *t, monitorLockRecord *r)
496 {
497         r->nextFree = t->ee.firstLR;
498         t->ee.firstLR = r;
499 }
500
501 static monitorLockRecord *appendToQueue(monitorLockRecord *queue, monitorLockRecord *lr)
502 {
503         monitorLockRecord *queuestart = queue;
504         if (!queue)
505                 return lr;
506         while (queue->queue)
507                 queue = queue->queue;
508         queue->queue = lr;
509         return queuestart;
510 }
511
512 static monitorLockRecord *moveMyLRToFront(threadobject *t, monitorLockRecord *lr)
513 {
514         monitorLockRecord *pred = NULL;
515         monitorLockRecord *firstLR = lr;
516         while (lr->owner != t) {
517                 pred = lr;
518                 lr = lr->queue;
519         }
520         if (!pred)
521                 return lr;
522         pred->queue = lr->queue;
523         lr->queue = firstLR;
524         lr->storedBits = firstLR->storedBits;
525         return lr;
526 }
527
528 static long getMetaLockSlow(threadobject *t, long predBits);
529 static void releaseMetaLockSlow(threadobject *t, long releaseBits);
530
531 static long getMetaLock(threadobject *t, java_objectheader *o)
532 {
533         long busyBits = makeLockBits(t, BUSY);
534         long lockBits = atomic_swap(&o->monitorBits, busyBits);
535         return lockState(lockBits) != BUSY ? lockBits : getMetaLockSlow(t, lockBits);
536 }
537
538 static long getMetaLockSlow(threadobject *t, long predBits)
539 {
540         long bits;
541         threadobject *pred = lockRecord(predBits);
542         pthread_mutex_lock(&pred->ee.metaLockMutex);
543         if (!pred->ee.bitsForGrab) {
544                 pred->ee.succ = t;
545                 do {
546                         pthread_cond_wait(&pred->ee.metaLockCond, &pred->ee.metaLockMutex);
547                 } while (!t->ee.gotMetaLockSlow);
548                 t->ee.gotMetaLockSlow = false;
549                 bits = t->ee.metaLockBits;
550         } else {
551                 bits = pred->ee.metaLockBits;
552                 pred->ee.bitsForGrab = false;
553                 pthread_cond_signal(&pred->ee.metaLockCond);
554         }
555         pthread_mutex_unlock(&pred->ee.metaLockMutex);
556         return bits;
557 }
558
559 static void releaseMetaLock(threadobject *t, java_objectheader *o, long releaseBits)
560 {
561         long busyBits = makeLockBits(t, BUSY);
562         long lockBits = compare_and_swap(&o->monitorBits, busyBits, releaseBits);
563         
564         if (lockBits != busyBits)
565                 releaseMetaLockSlow(t, releaseBits);
566 }
567
568 static void releaseMetaLockSlow(threadobject *t, long releaseBits)
569 {
570         pthread_mutex_lock(&t->ee.metaLockMutex);
571         if (t->ee.succ) {
572                 assert(!t->ee.succ->ee.bitsForGrab);
573                 assert(!t->ee.bitsForGrab);
574                 assert(!t->ee.succ->ee.gotMetaLockSlow);
575                 t->ee.succ->ee.metaLockBits = releaseBits;
576                 t->ee.succ->ee.gotMetaLockSlow = true;
577                 t->ee.succ = NULL;
578                 pthread_cond_signal(&t->ee.metaLockCond);
579         } else {
580                 t->ee.metaLockBits = releaseBits;
581                 t->ee.bitsForGrab = true;
582                 do {
583                         pthread_cond_wait(&t->ee.metaLockCond, &t->ee.metaLockMutex);
584                 } while (t->ee.bitsForGrab);
585         }
586         pthread_mutex_unlock(&t->ee.metaLockMutex);
587 }
588
589 static void monitorEnterSlow(threadobject *t, java_objectheader *o, long r);
590
591 void monitorEnter(threadobject *t, java_objectheader *o)
592 {
593         long r = getMetaLock(t, o);
594         int state = lockState(r);
595
596         if (state == NEUTRAL) {
597                 monitorLockRecord *lr = allocLockRecord(t);
598                 lr->storedBits = r;
599                 releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
600         } else if (state == LOCKED) {
601                 monitorLockRecord *ownerLR = lockRecord(r);
602                 if (ownerLR->owner == t) {
603                         ownerLR->lockCount++;
604                         releaseMetaLock(t, o, r);
605                 } else {
606                         monitorLockRecord *lr = allocLockRecord(t);
607                         ownerLR->queue = appendToQueue(ownerLR->queue, lr);
608                         monitorEnterSlow(t, o, r);
609                 }
610         } else if (state == WAITERS) {
611                 monitorLockRecord *lr = allocLockRecord(t);
612                 monitorLockRecord *firstWaiterLR = lockRecord(r);
613                 lr->queue = firstWaiterLR;
614                 lr->storedBits = firstWaiterLR->storedBits;
615                 releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
616         }
617 }
618
619 static void monitorEnterSlow(threadobject *t, java_objectheader *o, long r)
620 {
621         monitorLockRecord *lr;
622         monitorLockRecord *firstWaiterLR;
623         while (lockState(r) == LOCKED) {
624                 pthread_mutex_lock(&t->ee.monitorLockMutex);
625                 releaseMetaLock(t, o, r);
626                 pthread_cond_wait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex);
627                 pthread_mutex_unlock(&t->ee.monitorLockMutex);
628                 r = getMetaLock(t, o);
629         }
630         assert(lockState(r) == WAITERS);
631         lr = moveMyLRToFront(t, lockRecord(r));
632         releaseMetaLock(t, o, makeLockBits(lr, LOCKED));
633 }
634
635 static void monitorExitSlow(threadobject *t, java_objectheader *o, monitorLockRecord *lr);
636
637 void monitorExit(threadobject *t, java_objectheader *o)
638 {
639         long r = getMetaLock(t, o);
640         monitorLockRecord *ownerLR = lockRecord(r);
641         int state = lockState(r);
642         if (state == LOCKED && ownerLR->owner == t) {
643                 assert(ownerLR->lockCount >= 1);
644                 if (ownerLR->lockCount == 1) {
645                         if (ownerLR->queue == NULL) {
646                                 assert(lockState(ownerLR->storedBits) == NEUTRAL);
647                                 releaseMetaLock(t, o, ownerLR->storedBits);
648                         } else {
649                                 ownerLR->queue->storedBits = ownerLR->storedBits;
650                                 monitorExitSlow(t, o, ownerLR->queue);
651                                 ownerLR->queue = NULL;
652                         }
653                         recycleLockRecord(t, ownerLR);
654                 } else {
655                         ownerLR->lockCount--;
656                         releaseMetaLock(t, o, r);
657                 }
658         } else {
659                 releaseMetaLock(t, o, r);
660                 panic("Illegal monitor exception");
661         }
662 }
663
664 static threadobject *wakeupEE(monitorLockRecord *lr)
665 {
666         while (lr->queue && lr->queue->owner->ee.isWaitingForNotify)
667                 lr = lr->queue;
668         return lr->owner;
669 }
670
671 static void monitorExitSlow(threadobject *t, java_objectheader *o, monitorLockRecord *lr)
672 {
673         threadobject *wakeEE = wakeupEE(lr);
674         if (wakeEE) {
675                 pthread_mutex_lock(&wakeEE->ee.monitorLockMutex);
676                 releaseMetaLock(t, o, makeLockBits(lr, WAITERS));
677                 pthread_cond_signal(&wakeEE->ee.monitorLockCond);
678                 pthread_mutex_unlock(&wakeEE->ee.monitorLockMutex);
679         } else {
680                 releaseMetaLock(t, o, makeLockBits(lr, WAITERS));
681         }
682 }
683
684 void monitorWait(threadobject *t, java_objectheader *o, s8 millis)
685 {
686         long r = getMetaLock(t, o);
687         monitorLockRecord *ownerLR = lockRecord(r);
688         int state = lockState(r);
689         if (state == LOCKED && ownerLR->owner == t) {
690                 pthread_mutex_lock(&t->ee.monitorLockMutex);
691                 t->ee.isWaitingForNotify = true;
692                 monitorExitSlow(t, o, ownerLR);
693                 if (millis == -1)
694                         pthread_cond_wait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex);
695                 else
696                         timedCondWait(&t->ee.monitorLockCond, &t->ee.monitorLockMutex, millis);
697                 t->ee.isWaitingForNotify = false;
698                 pthread_mutex_unlock(&t->ee.monitorLockMutex);
699                 r = getMetaLock(t, o);
700                 monitorEnterSlow(t, o, r);
701         } else {
702                 releaseMetaLock(t, o, r);
703                 panic("Illegal monitor exception");
704         }
705 }
706
707 static void notifyOneOrAll(threadobject *t, java_objectheader *o, bool one)
708 {
709         long r = getMetaLock(t, o);
710         monitorLockRecord *ownerLR = lockRecord(r);
711         int state = lockState(r);
712         if (state == LOCKED && ownerLR->owner == t) {
713                 monitorLockRecord *q = ownerLR->queue;
714                 while (q) {
715                         if (q->owner->ee.isWaitingForNotify) {
716                                 q->owner->ee.isWaitingForNotify = false;
717                                 if (one)
718                                         break;
719                         }
720                         q = q->queue;
721                 }
722                 releaseMetaLock(t, o, r);
723         } else {
724                 releaseMetaLock(t, o, r);
725                 panic("Illegal monitor exception");
726         }
727 }
728
729 void wait_cond_for_object(java_objectheader *o, s8 time)
730 {
731         threadobject *t = (threadobject*) THREADOBJECT;
732         monitorWait(t, o, time);
733 }
734
735 void signal_cond_for_object(java_objectheader *o)
736 {
737         threadobject *t = (threadobject*) THREADOBJECT;
738         notifyOneOrAll(t, o, true);
739 }
740
741 void broadcast_cond_for_object(java_objectheader *o)
742 {
743         threadobject *t = (threadobject*) THREADOBJECT;
744         notifyOneOrAll(t, o, false);
745 }
746
747 #endif