d3e3cc758509f23976d3e4ece3729a1ef8138f1c
[mono.git] / mono / metadata / monitor.c
1 /*
2  * monitor.c:  Monitor locking functions
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/monitor.h>
14 #include <mono/metadata/threads-types.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/threads.h>
17 #include <mono/io-layer/io-layer.h>
18 #include <mono/metadata/object-internals.h>
19
20 #include <mono/os/gc_wrapper.h>
21
22 /*#define LOCK_DEBUG(a) do { a; } while (0)*/
23 #define LOCK_DEBUG(a)
24
25 /*
26  * The monitor implementation here is based on
27  * http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf and
28  * http://www.research.ibm.com/people/d/dfb/papers/Bacon98Thin.ps
29  *
30  * The Dice paper describes a technique for saving lock record space
31  * by returning records to a free list when they become unused.  That
32  * sounds like unnecessary complexity to me, though if it becomes
33  * clear that unused lock records are taking up lots of space or we
34  * need to shave more time off by avoiding a malloc then we can always
35  * implement the free list idea later.  The timeout parameter to
36  * try_enter voids some of the assumptions about the reference count
37  * field in Dice's implementation too.  In his version, the thread
38  * attempting to lock a contended object will block until it succeeds,
39  * so the reference count will never be decremented while an object is
40  * locked.
41  *
42  * Bacon's thin locks have a fast path that doesn't need a lock record
43  * for the common case of locking an unlocked or shallow-nested
44  * object, but the technique relies on encoding the thread ID in 15
45  * bits (to avoid too much per-object space overhead.)  Unfortunately
46  * I don't think it's possible to reliably encode a pthread_t into 15
47  * bits. (The JVM implementation used seems to have a 15-bit
48  * per-thread identifier available.)
49  *
50  * This implementation then combines Dice's basic lock model with
51  * Bacon's simplification of keeping a lock record for the lifetime of
52  * an object.
53  */
54
55
56 static void 
57 mon_finalize (void *o, void *unused)
58 {
59         MonoThreadsSync *mon = o;
60         
61         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync %p", mon));
62
63         if (mon->entry_sem != NULL) {
64                 CloseHandle (mon->entry_sem);
65         }
66         /* If this isn't empty then something is seriously broken - it
67          * means a thread is still waiting on the object that owned
68          * this lock, but the object has been finalized.
69          */
70         g_assert (mon->wait_list == NULL);
71 }
72
73 static MonoThreadsSync *
74 mon_new (guint32 id)
75 {
76         MonoThreadsSync *new;
77         
78 #if HAVE_BOEHM_GC
79         new = GC_MALLOC (sizeof (MonoThreadsSync));
80         GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL);
81 #else
82         /* This should be freed when the object that owns it is
83          * deleted
84          */
85         new = g_new0 (MonoThreadsSync, 1);
86 #endif
87         new->owner = id;
88         new->nest = 1;
89         
90         return new;
91 }
92
93 /* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
94  * is requested. In this case it returns -1.
95  */ 
96 static gint32 
97 mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
98 {
99         MonoThreadsSync *mon;
100         guint32 id = GetCurrentThreadId ();
101         HANDLE sem;
102         guint32 then = 0, now, delta;
103         guint32 waitms;
104         guint32 ret;
105         
106         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
107                   ": (%d) Trying to lock object %p (%d ms)", id, obj, ms));
108
109 retry:
110         mon = obj->synchronisation;
111
112         /* If the object has never been locked... */
113         if (mon == NULL) {
114                 mon = mon_new (id);
115                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) {
116                         /* Successfully locked */
117                         return 1;
118                 } else {
119                         /* Another thread got in first, so try again.
120                          * GC will take care of the monitor record
121                          */
122 #ifndef HAVE_BOEHM_GC
123                         mon_finalize (mon, NULL);
124 #endif
125                         goto retry;
126                 }
127         }
128
129         /* If the object is currently locked by this thread... */
130         if (mon->owner == id) {
131                 mon->nest++;
132                 return 1;
133         }
134
135         /* If the object has previously been locked but isn't now... */
136
137         /* This case differs from Dice's case 3 because we don't
138          * deflate locks or cache unused lock records
139          */
140         if (mon->owner == 0) {
141                 /* Try to install our ID in the owner field, nest
142                  * should have been left at 1 by the previous unlock
143                  * operation
144                  */
145                 if (InterlockedCompareExchange (&mon->owner, id, 0) == 0) {
146                         /* Success */
147                         g_assert (mon->nest == 1);
148                         return 1;
149                 } else {
150                         /* Trumped again! */
151                         goto retry;
152                 }
153         }
154
155         /* The object must be locked by someone else... */
156
157         /* If ms is 0 we don't block, but just fail straight away */
158         if (ms == 0) {
159                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out, returning FALSE", id));
160                 return 0;
161         }
162
163         /* The slow path begins here.  We need to make sure theres a
164          * semaphore handle (creating it if necessary), and block on
165          * it
166          */
167         if (mon->entry_sem == NULL) {
168                 /* Create the semaphore */
169                 sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
170                 if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
171                         /* Someone else just put a handle here */
172                         CloseHandle (sem);
173                 }
174         }
175
176         /* If we need to time out, record a timestamp and adjust ms,
177          * because WaitForSingleObject doesn't tell us how long it
178          * waited for.
179          *
180          * Don't block forever here, because theres a chance the owner
181          * thread released the lock while we were creating the
182          * semaphore: we would not get the wakeup.  Using the event
183          * handle technique from pulse/wait would involve locking the
184          * lock struct and therefore slowing down the fast path.
185          */
186         if (ms != INFINITE) {
187                 then = GetTickCount ();
188                 if (ms < 100) {
189                         waitms = ms;
190                 } else {
191                         waitms = 100;
192                 }
193         } else {
194                 waitms = 100;
195         }
196         
197         InterlockedIncrement (&mon->entry_count);
198         ret = WaitForSingleObjectEx (mon->entry_sem, waitms, allow_interruption);
199         InterlockedDecrement (&mon->entry_count);
200
201         if (ms != INFINITE) {
202                 now = GetTickCount ();
203                 
204                 if (now < then) {
205                         /* The counter must have wrapped around */
206                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
207                                    ": wrapped around! now=0x%x then=0x%x", now, then));
208                         
209                         now += (0xffffffff - then);
210                         then = 0;
211
212                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": wrap rejig: now=0x%x then=0x%x delta=0x%x", now, then, now-then));
213                 }
214                 
215                 delta = now - then;
216                 if (delta >= ms) {
217                         ms = 0;
218                 } else {
219                         ms -= delta;
220                 }
221
222                 if ((ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) && ms > 0) {
223                         /* More time left */
224                         goto retry;
225                 }
226         } else {
227                 if (ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) {
228                         /* Infinite wait, so just try again */
229                         goto retry;
230                 }
231         }
232         
233         if (ret == WAIT_OBJECT_0) {
234                 /* retry from the top */
235                 goto retry;
236         }
237         
238         /* We must have timed out */
239         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out waiting, returning FALSE", id));
240
241         if (ret == WAIT_IO_COMPLETION)
242                 return -1;
243         else 
244                 return 0;
245 }
246
247 gboolean 
248 mono_monitor_enter (MonoObject *obj)
249 {
250         return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
251 }
252
253 gboolean 
254 mono_monitor_try_enter (MonoObject *obj, guint32 ms)
255 {
256         return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
257 }
258
259 void 
260 mono_monitor_exit (MonoObject *obj)
261 {
262         MonoThreadsSync *mon;
263         guint32 nest;
264         
265         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", GetCurrentThreadId (), obj));
266
267         mon = obj->synchronisation;
268
269         if (mon == NULL) {
270                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
271                 return;
272         }
273         if (mon->owner != GetCurrentThreadId ()) {
274                 return;
275         }
276         
277         nest = mon->nest - 1;
278         if (nest == 0) {
279                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
280                           ": (%d) Object %p is now unlocked", GetCurrentThreadId (), obj));
281         
282                 /* object is now unlocked, leave nest==1 so we don't
283                  * need to set it when the lock is reacquired
284                  */
285                 mon->owner = 0;
286
287                 /* Do the wakeup stuff.  It's possible that the last
288                  * blocking thread gave up waiting just before we
289                  * release the semaphore resulting in a futile wakeup
290                  * next time there's contention for this object, but
291                  * it means we don't have to waste time locking the
292                  * struct.
293                  */
294                 if (mon->entry_count > 0) {
295                         ReleaseSemaphore (mon->entry_sem, 1, NULL);
296                 }
297         } else {
298                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
299                           ": (%d) Object %p is now locked %d times", GetCurrentThreadId (), obj, nest));
300                 mon->nest = nest;
301         }
302 }
303
304 gboolean 
305 ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
306 {
307         gint32 res;
308
309         do {
310                 res = mono_monitor_try_enter_internal (obj, ms, TRUE);
311                 if (res == -1)
312                         mono_thread_interruption_checkpoint ();
313         } while (res == -1);
314         
315         return res == 1;
316 }
317
318 void 
319 ves_icall_System_Threading_Monitor_Monitor_exit (MonoObject *obj)
320 {
321         mono_monitor_exit (obj);
322 }
323
324 gboolean 
325 ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
326 {
327         MonoThreadsSync *mon;
328         
329         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
330                   ": Testing if %p is owned by thread %d", obj, GetCurrentThreadId()));
331
332         mon = obj->synchronisation;
333         if (mon == NULL) {
334                 return FALSE;
335         }
336         
337         if(mon->owner==GetCurrentThreadId ()) {
338                 return(TRUE);
339         }
340         
341         return(FALSE);
342 }
343
344 gboolean 
345 ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj)
346 {
347         MonoThreadsSync *mon;
348
349         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
350                   ": (%d) Testing if %p is owned by any thread", GetCurrentThreadId (), obj));
351         
352         mon = obj->synchronisation;
353         if (mon == NULL) {
354                 return FALSE;
355         }
356         
357         if (mon->owner != 0) {
358                 return TRUE;
359         }
360         
361         return FALSE;
362 }
363
364 /* All wait list manipulation in the pulse, pulseall and wait
365  * functions happens while the monitor lock is held, so we don't need
366  * any extra struct locking
367  */
368
369 void
370 ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj)
371 {
372         MonoThreadsSync *mon;
373         
374         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing %p", 
375                 GetCurrentThreadId (), obj));
376         
377         mon = obj->synchronisation;
378         if (mon == NULL) {
379                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
380                 return;
381         }
382         if (mon->owner != GetCurrentThreadId ()) {
383                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
384                 return;
385         }
386
387         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
388                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
389         
390         if (mon->wait_list != NULL) {
391                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
392                           ": (%d) signalling and dequeuing handle %p",
393                           GetCurrentThreadId (), mon->wait_list->data));
394         
395                 SetEvent (mon->wait_list->data);
396                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
397         }
398 }
399
400 void
401 ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj)
402 {
403         MonoThreadsSync *mon;
404         
405         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing all %p",
406                   GetCurrentThreadId (), obj));
407
408         mon = obj->synchronisation;
409         if (mon == NULL) {
410                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
411                 return;
412         }
413         if (mon->owner != GetCurrentThreadId ()) {
414                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
415                 return;
416         }
417
418         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
419                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
420
421         while (mon->wait_list != NULL) {
422                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
423                           ": (%d) signalling and dequeuing handle %p",
424                           GetCurrentThreadId (), mon->wait_list->data));
425         
426                 SetEvent (mon->wait_list->data);
427                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
428         }
429 }
430
431 gboolean
432 ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
433 {
434         MonoThreadsSync *mon;
435         HANDLE event;
436         guint32 nest;
437         guint32 ret;
438         gboolean success = FALSE;
439         gint32 regain;
440         MonoThread *thread = mono_thread_current ();
441
442         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
443                   ": (%d) Trying to wait for %p with timeout %dms",
444                   GetCurrentThreadId (), obj, ms));
445         
446         mon = obj->synchronisation;
447         if (mon == NULL) {
448                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
449                 return FALSE;
450         }
451         if (mon->owner != GetCurrentThreadId ()) {
452                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
453                 return FALSE;
454         }
455         
456         event = CreateEvent (NULL, FALSE, FALSE, NULL);
457         if (event == NULL) {
458                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event"));
459                 return FALSE;
460         }
461         
462         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p",
463                   GetCurrentThreadId (), event));
464
465         mono_monitor_enter (thread->synch_lock);
466         thread->state |= ThreadState_WaitSleepJoin;
467         mono_monitor_exit (thread->synch_lock);
468
469         mon->wait_list = g_slist_append (mon->wait_list, event);
470         
471         /* Save the nest count, and release the lock */
472         nest = mon->nest;
473         mon->nest = 1;
474         mono_monitor_exit (obj);
475
476         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocked %p lock %p",
477                   GetCurrentThreadId (), obj, mon));
478
479         /* There's no race between unlocking mon and waiting for the
480          * event, because auto reset events are sticky, and this event
481          * is private to this thread.  Therefore even if the event was
482          * signalled before we wait, we still succeed.
483          */
484         ret = WaitForSingleObjectEx (event, ms, TRUE);
485
486         /* Reset the thread state fairly early, so we don't have to worry
487          * about the monitor error checking
488          */
489         mono_monitor_enter (thread->synch_lock);
490         thread->state &= ~ThreadState_WaitSleepJoin;
491         mono_monitor_exit (thread->synch_lock);
492         
493         if (mono_thread_interruption_requested ()) {
494                 CloseHandle (event);
495                 return FALSE;
496         }
497
498         /* Regain the lock with the previous nest count */
499         do {
500                 regain = mono_monitor_try_enter_internal (obj, INFINITE, TRUE);
501                 if (regain == -1) 
502                         mono_thread_interruption_checkpoint ();
503         } while (regain == -1);
504
505         if (regain == 0) {
506                 /* Something went wrong, so throw a
507                  * SynchronizationLockException
508                  */
509                 CloseHandle (event);
510                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to regain lock"));
511                 return FALSE;
512         }
513
514         mon->nest = nest;
515
516         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Regained %p lock %p",
517                   GetCurrentThreadId (), obj, mon));
518
519         if (ret == WAIT_TIMEOUT) {
520                 /* Poll the event again, just in case it was signalled
521                  * while we were trying to regain the monitor lock
522                  */
523                 ret = WaitForSingleObjectEx (event, 0, FALSE);
524         }
525
526         /* Pulse will have popped our event from the queue if it signalled
527          * us, so we only do it here if the wait timed out.
528          *
529          * This avoids a race condition where the thread holding the
530          * lock can Pulse several times before the WaitForSingleObject
531          * returns.  If we popped the queue here then this event might
532          * be signalled more than once, thereby starving another
533          * thread.
534          */
535         
536         if (ret == WAIT_OBJECT_0) {
537                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Success",
538                           GetCurrentThreadId ()));
539                 success = TRUE;
540         } else {
541                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed, dequeuing handle %p",
542                           GetCurrentThreadId (), event));
543                 /* No pulse, so we have to remove ourself from the
544                  * wait queue
545                  */
546                 mon->wait_list = g_slist_remove (mon->wait_list, event);
547         }
548         CloseHandle (event);
549         
550         return success;
551 }
552