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