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