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