[project @ 2005-10-24 09:37:08 by simonmar]
[ghc-hetmet.git] / ghc / rts / win32 / AsyncIO.c
index fabe85b..12893f3 100644 (file)
@@ -9,6 +9,8 @@
 #include <windows.h>
 #include <stdio.h>
 #include "Schedule.h"
+#include "RtsFlags.h"
+#include "Capability.h"
 #include "win32/AsyncIO.h"
 #include "win32/IOManager.h"
 
@@ -48,6 +50,7 @@ static HANDLE           abandon_req_wait;
 static HANDLE           wait_handles[2];
 static CompletedReq     completedTable[MAX_REQUESTS];
 static int              completed_hw;
+static HANDLE           completed_table_sema;
 static int              issued_reqs;
 
 static void
@@ -57,11 +60,22 @@ onIOComplete(unsigned int reqID,
             void* buf STG_UNUSED,
             int   errCode)
 {
-    /* Deposit result of request in queue/table */
+    DWORD dwRes;
+    /* Deposit result of request in queue/table..when there's room. */
+    dwRes = WaitForSingleObject(completed_table_sema, INFINITE);
+    switch (dwRes) {
+    case WAIT_OBJECT_0:
+       break;
+    default:
+       /* Not likely */
+       fprintf(stderr, "onIOComplete: failed to grab table semaphore, dropping request 0x%x\n", reqID);
+       fflush(stderr);
+       return;
+    }
     EnterCriticalSection(&queue_lock);
     if (completed_hw == MAX_REQUESTS) {
-       /* Not likely */
-       fprintf(stderr, "Request table overflow (%d); dropping.\n", reqID);
+       /* Shouldn't happen */
+       fprintf(stderr, "onIOComplete: ERROR -- Request table overflow (%d); dropping.\n", reqID);
        fflush(stderr);
     } else {
 #if 0
@@ -76,7 +90,7 @@ onIOComplete(unsigned int reqID,
        issued_reqs--;
        if (completed_hw == 1) {
            /* The event is used to wake up the scheduler thread should it
-            * be blocked waiting for requests to complete. It reset once
+            * be blocked waiting for requests to complete. The event resets once
             * that thread has cleared out the request queue/table.
             */
            SetEvent(completed_req_event);
@@ -145,8 +159,15 @@ startupAsyncIO()
     wait_handles[0] = completed_req_event;
     wait_handles[1] = abandon_req_wait;
     completed_hw = 0;
-    return ( completed_req_event != INVALID_HANDLE_VALUE &&
-            abandon_req_wait    != INVALID_HANDLE_VALUE );
+    if ( !(completed_table_sema = CreateSemaphore (NULL, MAX_REQUESTS, MAX_REQUESTS, NULL)) ) {
+       DWORD rc = GetLastError();
+       fprintf(stderr, "startupAsyncIO: CreateSemaphore failed 0x%x\n", rc);
+       fflush(stderr);
+    }
+
+    return ( completed_req_event  != INVALID_HANDLE_VALUE &&
+            abandon_req_wait     != INVALID_HANDLE_VALUE &&
+            completed_table_sema != NULL );
 }
 
 void
@@ -175,6 +196,9 @@ shutdownAsyncIO()
 int
 awaitRequests(rtsBool wait)
 {
+#ifndef THREADED_RTS
+  // none of this is actually used in the threaded RTS
+
 start:
 #if 0
     fprintf(stderr, "awaitRequests(): %d %d %d\n", issued_reqs, completed_hw, wait);
@@ -182,8 +206,13 @@ start:
 #endif
     EnterCriticalSection(&queue_lock);
     /* Nothing immediately available & we won't wait */
-    if ((!wait && completed_hw == 0) || 
-       (issued_reqs == 0 && completed_hw == 0)) {
+    if ((!wait && completed_hw == 0)
+#if 0
+       // If we just return when wait==rtsFalse, we'll go into a busy
+       // wait loop, so I disabled this condition --SDM 18/12/2003
+       (issued_reqs == 0 && completed_hw == 0)
+#endif
+       ) {
        LeaveCriticalSection(&queue_lock);
        return 0;
     }
@@ -194,16 +223,23 @@ start:
            DWORD dwRes = WaitForMultipleObjects(2, wait_handles, FALSE, INFINITE);
            switch (dwRes) {
            case WAIT_OBJECT_0:
+               /* a request was completed */
                break;
            case WAIT_OBJECT_0 + 1:
            case WAIT_TIMEOUT:
+               /* timeout (unlikely) or told to abandon waiting */
                return 0;
+           case WAIT_FAILED: {
+               DWORD dw = GetLastError();
+               fprintf(stderr, "awaitRequests: wait failed -- error code: %lu\n", dw); fflush(stderr);
+               return 0;
+           }
            default:
                fprintf(stderr, "awaitRequests: unexpected wait return code %lu\n", dwRes); fflush(stderr);
                return 0;
            }
        } else {
-           return 0; /* cannot happen */
+           return 0;
        }
        goto start;
     } else {
@@ -222,7 +258,6 @@ start:
             *
             */
            unsigned int rID = completedTable[i].reqID;
-           prev = NULL;
            
            prev = NULL;
            for(tso = blocked_queue_hd ; tso != END_TSO_QUEUE; prev = tso, tso = tso->link) {
@@ -262,12 +297,19 @@ start:
                    break;
                }
            }
+           /* Signal that there's completed table slots available */
+           if ( !ReleaseSemaphore(completed_table_sema, 1, NULL) ) {
+               DWORD dw = GetLastError();
+               fprintf(stderr, "awaitRequests: failed to signal semaphore (error code=0x%x)\n", dw);
+               fflush(stderr);
+           }
        }
        completed_hw = 0;
        ResetEvent(completed_req_event);
        LeaveCriticalSection(&queue_lock);
        return 1;
     }
+#endif /* !THREADED_RTS */
 }
 
 /*
@@ -277,11 +319,27 @@ start:
  * to complete (via awaitRequests().)
  */
 void
-abandonRequestWait()
+abandonRequestWait( void )
 {
     /* the event is auto-reset, but in case there's no thread
      * already waiting on the event, we want to return it to
      * a non-signalled state.
+     *
+     * Careful!  There is no synchronisation between
+     * abandonRequestWait and awaitRequest, which means that
+     * abandonRequestWait might be called just before a thread
+     * goes into a wait, and we miss the abandon signal.  So we
+     * must SetEvent() here rather than PulseEvent() to ensure
+     * that the event isn't lost.  We can re-optimise by resetting
+     * the event somewhere safe if we know the event has been
+     * properly serviced (see resetAbandon() below).  --SDM 18/12/2003
      */
-    PulseEvent(abandon_req_wait);
+    SetEvent(abandon_req_wait);
 }
+
+void
+resetAbandonRequestWait( void )
+{
+    ResetEvent(abandon_req_wait);
+}
+