2004-07-05 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / metadata / monitor.c
index 6ec0c3af418a26516ddc0822f8deeaf816e03c98..0d17fb50a02c1b49f79a193a2f51bd71cb567359 100644 (file)
@@ -13,6 +13,7 @@
 #include <mono/metadata/monitor.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/exception.h>
+#include <mono/metadata/threads.h>
 #include <mono/io-layer/io-layer.h>
 
 #include <mono/os/gc_wrapper.h>
@@ -87,12 +88,15 @@ static MonoThreadsSync *mon_new(guint32 id)
        return(new);
 }
 
-gboolean mono_monitor_try_enter (MonoObject *obj, guint32 ms)
+/* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
+ * is requested. In this case it returns -1.
+ */ 
+static gint32 mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
 {
        MonoThreadsSync *mon;
        guint32 id=GetCurrentThreadId ();
        HANDLE sem;
-       guint32 then, now, delta;
+       guint32 then=0, now, delta;
        guint32 waitms;
        guint32 ret;
        
@@ -109,7 +113,7 @@ retry:
                mon=mon_new(id);
                if(InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL)==NULL) {
                        /* Successfully locked */
-                       return(TRUE);
+                       return(1);
                } else {
                        /* Another thread got in first, so try again.
                         * GC will take care of the monitor record
@@ -124,7 +128,7 @@ retry:
        /* If the object is currently locked by this thread... */
        if(mon->owner==id) {
                mon->nest++;
-               return(TRUE);
+               return(1);
        }
 
        /* If the object has previously been locked but isn't now... */
@@ -140,7 +144,7 @@ retry:
                if(InterlockedCompareExchange (&mon->owner, id, 0)==0) {
                        /* Success */
                        g_assert (mon->nest==1);
-                       return(TRUE);
+                       return(1);
                } else {
                        /* Trumped again! */
                        goto retry;
@@ -156,7 +160,7 @@ retry:
                           ": (%d) timed out, returning FALSE", id);
 #endif
 
-               return(FALSE);
+               return(0);
        }
 
        /* The slow path begins here.  We need to make sure theres a
@@ -194,9 +198,9 @@ retry:
        }
        
        InterlockedIncrement (&mon->entry_count);
-       ret=WaitForSingleObject (mon->entry_sem, waitms);
+       ret=WaitForSingleObjectEx (mon->entry_sem, waitms, allow_interruption);
        InterlockedDecrement (&mon->entry_count);
-       
+
        if(ms!=INFINITE) {
                now=GetTickCount ();
                
@@ -223,12 +227,12 @@ retry:
                        ms-=delta;
                }
 
-               if(ret==WAIT_TIMEOUT && ms>0) {
+               if((ret==WAIT_TIMEOUT || (ret==WAIT_IO_COMPLETION && !allow_interruption)) && ms>0) {
                        /* More time left */
                        goto retry;
                }
        } else {
-               if(ret==WAIT_TIMEOUT) {
+               if(ret==WAIT_TIMEOUT || (ret==WAIT_IO_COMPLETION && !allow_interruption)) {
                        /* Infinite wait, so just try again */
                        goto retry;
                }
@@ -245,7 +249,18 @@ retry:
                   ": (%d) timed out waiting, returning FALSE", id);
 #endif
 
-       return(FALSE);
+       if (ret==WAIT_IO_COMPLETION) return(-1);
+       else return(0);
+}
+
+gboolean mono_monitor_enter (MonoObject *obj)
+{
+       return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
+}
+
+gboolean mono_monitor_try_enter (MonoObject *obj, guint32 ms)
+{
+       return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
 }
 
 void mono_monitor_exit (MonoObject *obj)
@@ -305,9 +320,17 @@ void mono_monitor_exit (MonoObject *obj)
 gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj,
                                                              guint32 ms)
 {
+       gint32 res;
        MONO_ARCH_SAVE_REGS;
 
-       return(mono_monitor_try_enter (obj, ms));
+       do {
+               res = mono_monitor_try_enter_internal (obj, ms, TRUE);
+               if (res == -1)
+                       mono_thread_interruption_checkpoint ();
+       }
+       while (res == -1);
+       
+       return(res == 1);
 }
 
 void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj)
@@ -455,7 +478,8 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
        HANDLE event;
        guint32 nest;
        guint32 ret;
-       gboolean success=FALSE, regain;
+       gboolean success=FALSE;
+       gint32 regain;
        
        MONO_ARCH_SAVE_REGS;
 
@@ -503,11 +527,22 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
         * is private to this thread.  Therefore even if the event was
         * signalled before we wait, we still succeed.
         */
-       ret=WaitForSingleObject (event, ms);
+       ret=WaitForSingleObjectEx (event, ms, TRUE);
        
+       if (mono_thread_interruption_requested ()) {
+               CloseHandle (event);
+               return(FALSE);
+       }
+
        /* Regain the lock with the previous nest count */
-       regain=mono_monitor_try_enter (obj, INFINITE);
-       if(regain==FALSE) {
+       do {
+               regain=mono_monitor_try_enter_internal (obj, INFINITE, TRUE);
+               if (regain == -1) 
+                       mono_thread_interruption_checkpoint ();
+       }
+       while (regain == -1);
+
+       if(regain==0) {
                /* Something went wrong, so throw a
                 * SynchronizationLockException
                 */
@@ -527,7 +562,7 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj,
                /* Poll the event again, just in case it was signalled
                 * while we were trying to regain the monitor lock
                 */
-               ret=WaitForSingleObject (event, 0);
+               ret=WaitForSingleObjectEx (event, 0, FALSE);
        }
 
        /* Pulse will have popped our event from the queue if it signalled