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