exceptionptr update for native threads
[cacao.git] / threads / locks.c
1 /* -*- mode: c; tab-width: 4; c-basic-offset: 4 -*- */
2 /*
3  * locks.c
4  * Manage locking system
5  * This include the mutex's and cv's.
6  *
7  * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
8  *
9  * See the file "license.terms" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
13  */
14
15 #include "config.h"
16
17 #include <assert.h>
18 #include <stdio.h>
19
20 #include "thread.h"
21 #include "locks.h"
22 #include "builtin.h"
23
24 #include "tables.h"
25 #include "native.h"
26 #include "loader.h"
27
28 static classinfo *class_java_lang_IllegalMonitorStateException;
29
30 extern thread* currentThread;
31
32 mutexHashEntry *mutexHashTable;
33 int mutexHashTableSize;
34 long mutexHashMask;
35
36 mutexHashEntry *mutexOverflowTable;
37 int mutexOverflowTableSize;
38 mutexHashEntry *firstFreeOverflowEntry = 0;
39
40 conditionHashEntry *conditionHashTable;
41 int conditionHashTableSize;
42 long conditionHashMask;
43
44 /*
45  * Init the tables.
46  */
47 void
48 initLocks (void)
49 {
50     int i;
51
52     mutexHashTableSize = MUTEX_HASH_TABLE_SIZE;
53     mutexHashTable = (mutexHashEntry*)malloc(sizeof(mutexHashEntry) * mutexHashTableSize);
54     mutexHashMask = (mutexHashTableSize - 1) << 3;
55
56     for (i = 0; i < mutexHashTableSize; ++i)
57     {
58                 mutexHashTable[i].object = 0;
59                 mutexHashTable[i].mutex.holder = 0;
60                 mutexHashTable[i].mutex.count = 0;
61                 mutexHashTable[i].mutex.muxWaiters = 0;
62                 mutexHashTable[i].conditionCount = 0;
63                 mutexHashTable[i].next = 0;
64     }
65
66     mutexOverflowTableSize = MUTEX_OVERFLOW_TABLE_SIZE;
67     mutexOverflowTable = (mutexHashEntry*)malloc(sizeof(mutexHashEntry)
68                                                                                                  * mutexOverflowTableSize);
69
70     firstFreeOverflowEntry = &mutexOverflowTable[0];
71
72     for (i = 0; i < mutexOverflowTableSize; ++i)
73     {
74                 mutexOverflowTable[i].object = 0;
75                 mutexOverflowTable[i].mutex.holder = 0;
76                 mutexOverflowTable[i].mutex.count = 0;
77                 mutexOverflowTable[i].mutex.muxWaiters = 0;
78                 mutexOverflowTable[i].conditionCount = 0;
79                 mutexOverflowTable[i].next = &mutexOverflowTable[i + 1];
80     }
81     mutexOverflowTable[i - 1].next = 0;
82
83     conditionHashTableSize = CONDITION_HASH_TABLE_SIZE;
84     conditionHashTable = (conditionHashEntry*)malloc(sizeof(conditionHashEntry)
85                                                                                                          * conditionHashTableSize);
86     conditionHashMask = (conditionHashTableSize - 1) << 3;
87
88     for (i = 0; i < conditionHashTableSize; ++i)
89     {
90                 conditionHashTable[i].object = 0;
91                 conditionHashTable[i].condition.cvWaiters = 0;
92                 conditionHashTable[i].condition.mux = 0;
93     }
94
95         /* Load exception classes */
96     loader_load_sysclass(&class_java_lang_IllegalMonitorStateException,
97                          utf_new_char("java/lang/IllegalMonitorStateException"));
98 }
99
100 /*
101  * Reorders part of the condition hash table. Must be called after an entry has been deleted.
102  */
103 void
104 reorderConditionHashTable (int begin)
105 {
106     while (conditionHashTable[begin].object != 0)
107     {
108         int hashValue = CONDITION_HASH_VALUE(conditionHashTable[begin].object);
109
110         if (hashValue != begin)
111         {
112             while (conditionHashTable[hashValue].object != 0)
113             {
114                 hashValue = CONDITION_HASH_SUCCESSOR(hashValue);
115                 if (hashValue == begin)
116                     break;
117             }
118             if (hashValue != begin)
119             {
120                 conditionHashTable[hashValue] = conditionHashTable[begin];
121                 conditionHashTable[begin].object = 0;
122                 conditionHashTable[begin].condition.cvWaiters = 0;
123                 conditionHashTable[begin].condition.mux = 0;
124             }
125         }
126
127         begin = CONDITION_HASH_SUCCESSOR(begin);
128     }
129 }
130
131 /*
132  * Looks up an entry in the condition hash table.
133  */
134 iCv*
135 conditionForObject (java_objectheader *object)
136 {
137     int hashValue;
138
139     intsDisable();
140
141     hashValue = CONDITION_HASH_VALUE(object);
142     while (conditionHashTable[hashValue].object != object
143                    && conditionHashTable[hashValue].object != 0)
144                 hashValue = CONDITION_HASH_SUCCESSOR(hashValue);
145
146     if (conditionHashTable[hashValue].object == 0)
147     {
148                 intsRestore();
149                 return 0;
150     }
151     
152     intsRestore();
153     return &conditionHashTable[hashValue].condition;
154 }
155
156 /*
157  * Adds a new entry in the condition hash table and returns a pointer to the condition
158  */
159 iCv*
160 addConditionForObject (java_objectheader *object)
161 {
162     int hashValue;
163
164     intsDisable();
165
166     hashValue = CONDITION_HASH_VALUE(object);
167     while (conditionHashTable[hashValue].object != 0)
168                 hashValue = CONDITION_HASH_SUCCESSOR(hashValue);
169
170     conditionHashTable[hashValue].object = object;
171
172     intsRestore();
173
174     return &conditionHashTable[hashValue].condition;
175 }
176
177 /*
178  * Removes an entry from the condition hash table.
179  */
180 void
181 removeConditionForObject (java_objectheader *object)
182 {
183     int hashValue;
184
185     intsDisable();
186
187     hashValue = CONDITION_HASH_VALUE(object);
188     while (conditionHashTable[hashValue].object != object)
189                 hashValue = CONDITION_HASH_SUCCESSOR(hashValue);
190
191     conditionHashTable[hashValue].object = 0;
192     conditionHashTable[hashValue].condition.cvWaiters = 0;
193     conditionHashTable[hashValue].condition.mux = 0;
194
195     reorderConditionHashTable(CONDITION_HASH_SUCCESSOR(hashValue));
196
197     intsRestore();
198 }
199
200 /*
201  * Returns the mutex entry for the specified object and increments its conditionCount.
202  */
203 mutexHashEntry*
204 conditionLockedMutexForObject (java_objectheader *object)
205 {
206     int hashValue;
207     mutexHashEntry *entry;
208
209     assert(object != 0);
210
211     intsDisable();
212
213     hashValue = MUTEX_HASH_VALUE(object);
214     entry = &mutexHashTable[hashValue];
215
216     if (entry->object != 0)
217     {
218                 if (entry->mutex.count == 0 && entry->conditionCount == 0)
219                 {
220                         entry->object = 0;
221                         entry->mutex.holder = 0;
222                         entry->mutex.count = 0;
223                         entry->mutex.muxWaiters = 0;
224                 }
225                 else
226                 {
227                         while (entry->next != 0 && entry->object != object)
228                                 entry = entry->next;
229
230                         if (entry->object != object)
231                         {
232                                 entry->next = firstFreeOverflowEntry;
233                                 firstFreeOverflowEntry = firstFreeOverflowEntry->next;
234                                 
235                                 entry = entry->next;
236                                 entry->object = 0;
237                                 entry->next = 0;
238                                 assert(entry->conditionCount == 0);
239                         }
240                 }
241     }
242
243     if (entry->object == 0)
244         entry->object = object;
245
246     ++entry->conditionCount;
247
248     intsRestore();
249
250     return entry;
251 }
252
253 /*
254  * Wait for the condition of an object to be signalled
255  */
256 void
257 wait_cond_for_object (java_objectheader *obj, s8 time)
258 {
259     iCv *condition;
260     mutexHashEntry *mutexEntry;
261
262     intsDisable();
263
264     mutexEntry = conditionLockedMutexForObject(obj);
265
266     condition = conditionForObject(obj);
267     if (condition == 0)
268                 condition = addConditionForObject(obj);
269
270     DBG( fprintf(stderr, "condition of %p is %p\n", obj, condition); );
271
272     internal_wait_cond(&mutexEntry->mutex, condition, time);
273
274     if (condition->cvWaiters == 0 && condition->mux == 0)
275                 removeConditionForObject(obj);
276     --mutexEntry->conditionCount;
277
278     intsRestore();
279 }
280
281 /*
282  * Signal the condition of an object
283  */
284 void
285 signal_cond_for_object (java_objectheader *obj)
286 {
287     iCv *condition;
288
289     intsDisable();
290
291     condition = conditionForObject(obj);
292     if (condition == 0)
293                 condition = addConditionForObject(obj);
294
295     DBG( fprintf(stderr, "condition of %p is %p\n", obj, condition); );
296     
297     internal_signal_cond(condition);
298
299     if (condition->cvWaiters == 0 && condition->mux == 0)
300                 removeConditionForObject(obj);
301
302     intsRestore();
303 }
304
305 /*
306  * Broadcast the condition of an object.
307  */
308 void
309 broadcast_cond_for_object (java_objectheader *obj)
310 {
311         intsDisable();
312         internal_broadcast_cond_for_object(obj);
313         intsRestore();
314 }
315
316 /*
317  * Internal: Broadcast the condition of an object.
318  */
319 void
320 internal_broadcast_cond_for_object (java_objectheader *obj)
321 {
322     iCv *condition;
323
324     condition = conditionForObject(obj);
325     if (condition == 0)
326                 condition = addConditionForObject(obj);
327
328     internal_broadcast_cond(condition);
329
330     if (condition->cvWaiters == 0 && condition->mux == 0)
331                 removeConditionForObject(obj);
332 }
333
334 /*
335  * Lock a mutex.
336  */
337 void
338 lock_mutex (iMux *mux)
339 {
340         intsDisable();
341         internal_lock_mutex(mux);
342         intsRestore();
343 }
344
345 /*
346  * Lock the mutex for an object.
347  */
348 void
349 lock_mutex_for_object (java_objectheader *obj)
350 {
351         intsDisable();
352         internal_lock_mutex_for_object(obj);
353         intsRestore();
354 }
355
356 /*
357  * Unlock a mutex.
358  */
359 void
360 unlock_mutex (iMux *mux)
361 {
362         intsDisable();
363         internal_unlock_mutex(mux);
364         intsRestore();
365 }
366
367 /*
368  * Unlock the mutex for an object.
369  */
370 void
371 unlock_mutex_for_object (java_objectheader *obj)
372 {
373         intsDisable();
374         internal_unlock_mutex_for_object(obj);
375         intsRestore();
376 }
377
378 /*
379  * Wait on a condition variable.
380  */
381 void
382 wait_cond (iMux *mux, iCv *cond, s8 timeout)
383 {
384         intsDisable();
385         internal_wait_cond(mux, cond, timeout);
386         intsRestore();
387 }
388
389 /*
390  * Signal a condition variable.
391  */
392 void
393 signal_cond (iCv *cond)
394 {
395         intsDisable();
396         internal_signal_cond(cond);
397         intsRestore();
398 }
399
400 /*
401  * Broadcast a condition variable.
402  */
403 void
404 broadcast_cond (iCv *cond)
405 {
406         intsDisable();
407         internal_broadcast_cond(cond);
408         intsRestore();
409 }
410
411 /*
412  * Internal: Lock a mutex.
413  */
414 void
415 internal_lock_mutex(iMux* mux)
416 {
417         assert(blockInts > 0);
418
419     if (mux->holder == 0)
420     {
421                 mux->holder = currentThread;
422                 mux->count = 1;
423                 DBG( fprintf(stderr, "set holder of %p to %p\n", mux, mux->holder); )
424     }
425     else if (mux->holder == currentThread)
426     {
427                 mux->count++;
428     }
429     else
430     {
431                 while (mux->holder != 0)
432                 {
433                         suspendOnQThread(currentThread, &mux->muxWaiters);
434                 }
435                 mux->holder = currentThread;
436                 mux->count = 1;
437     }
438 }
439
440 /*
441  * Internal: Release a mutex.
442  */
443 void
444 internal_unlock_mutex(iMux* mux)
445 {
446     thread* tid;
447
448         assert(blockInts > 0);
449
450     assert(mux->holder == currentThread);
451     
452     mux->count--;
453     if (mux->count == 0)
454     {
455                 mux->holder = 0;
456                 if (mux->muxWaiters != 0)
457                 {
458                         tid = mux->muxWaiters;
459                         mux->muxWaiters = tid->next;
460                         iresumeThread(tid);
461                 }
462     }
463 }
464
465 /*
466  * Internal: Wait on a conditional variable.
467  *  (timeout currently ignored)
468  */
469 void
470 internal_wait_cond(iMux* mux, iCv* cv, s8 timeout)
471 {
472     int count;
473     thread* tid;
474
475     DBG( fprintf(stderr, "waiting on %p\n", cv); );
476
477     if (mux->holder != currentThread) {
478                 *exceptionptr = native_new_and_init(class_java_lang_IllegalMonitorStateException);
479     }
480
481         assert(blockInts > 0);
482
483     count = mux->count;
484     mux->holder = 0;
485     mux->count = 0;
486     cv->mux = mux;
487
488     /* If there's anyone waiting here, wake them up */
489     if (mux->muxWaiters != 0) {
490                 tid = mux->muxWaiters;
491                 mux->muxWaiters = tid->next;
492                 iresumeThread(tid);
493     }
494
495     /* Suspend, and keep suspended until I re-get the lock */
496     suspendOnQThread(currentThread, &cv->cvWaiters);
497     while (mux->holder != 0) {
498                 DBG( fprintf(stderr, "woke up\n"); );
499                 suspendOnQThread(currentThread, &mux->muxWaiters);
500     }
501
502     mux->holder = currentThread;
503     mux->count = count;
504 }
505
506 /*
507  * Internal: Wake one thread on a conditional variable.
508  */
509 void
510 internal_signal_cond (iCv* cv)
511 {
512     thread* tid;
513
514     DBG( fprintf(stderr, "signalling on %p\n", cv); );
515
516     /* If 'mux' isn't set then we've never waited on this object. */
517     if (cv->mux == 0) {
518                 return;
519     }
520
521     if (cv->mux->holder != currentThread) {
522                 *exceptionptr = native_new_and_init(class_java_lang_IllegalMonitorStateException);
523     }
524
525         assert(blockInts > 0);
526
527     /* Remove one thread from cv list */
528     if (cv->cvWaiters != 0) {
529                 DBG( fprintf(stderr, "releasing a waiter\n"); );
530
531                 tid = cv->cvWaiters;
532                 cv->cvWaiters = tid->next;
533
534                 /* Place it on mux list */
535                 tid->next = cv->mux->muxWaiters;
536                 cv->mux->muxWaiters = tid;
537     }
538 }
539
540 /*
541  * Internal: Wake all threads on a conditional variable.
542  */
543 void
544 internal_broadcast_cond (iCv* cv)
545 {
546     thread** tidp;
547
548     /* If 'mux' isn't set then we've never waited on this object. */
549     if (cv->mux == 0) {
550                 return;
551     }
552
553     if (cv->mux->holder != currentThread) {
554                 *exceptionptr = native_new_and_init(class_java_lang_IllegalMonitorStateException);
555     }
556
557         assert(blockInts > 0);
558
559     /* Find the end of the cv list */
560     if (cv->cvWaiters) {
561                 for (tidp = &cv->cvWaiters; *tidp != 0; tidp = &(*tidp)->next)
562                         ;
563
564                 /* Place entire cv list on mux list */
565                 (*tidp) = cv->mux->muxWaiters;
566                 cv->mux->muxWaiters = cv->cvWaiters;
567                 cv->cvWaiters = 0;
568     }
569 }