diff options
author | Tamar Christina <tamar@zhox.com> | 2019-06-16 21:30:14 +0100 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2020-07-15 16:41:01 -0400 |
commit | 4bf542bf1cdf2fa468457fc0af21333478293476 (patch) | |
tree | 8378f0fa182d8e829e15fc5d102ba01aa8bd038e /rts | |
parent | 050da6dd42d0cb293c7fce4a5ccdeb5abe1aadb4 (diff) | |
download | haskell-4bf542bf1cdf2fa468457fc0af21333478293476.tar.gz |
winio: Multiple refactorings and support changes.
Diffstat (limited to 'rts')
-rw-r--r-- | rts/FileLock.c | 24 | ||||
-rw-r--r-- | rts/Prelude.h | 8 | ||||
-rw-r--r-- | rts/RtsStartup.c | 16 | ||||
-rw-r--r-- | rts/RtsSymbols.c | 22 | ||||
-rw-r--r-- | rts/package.conf.in | 8 | ||||
-rw-r--r-- | rts/rts.cabal.in | 17 | ||||
-rw-r--r-- | rts/win32/AsyncIO.c | 384 | ||||
-rw-r--r-- | rts/win32/AsyncIO.h | 24 | ||||
-rw-r--r-- | rts/win32/AwaitEvent.c | 16 | ||||
-rw-r--r-- | rts/win32/ConsoleHandler.c | 7 | ||||
-rw-r--r-- | rts/win32/ConsoleHandler.h | 14 | ||||
-rw-r--r-- | rts/win32/IOManager.c | 3 | ||||
-rw-r--r-- | rts/win32/IOManager.h | 5 | ||||
-rw-r--r-- | rts/win32/ThrIOManager.c | 60 | ||||
-rw-r--r-- | rts/win32/libHSbase.def | 2 |
15 files changed, 134 insertions, 476 deletions
diff --git a/rts/FileLock.c b/rts/FileLock.c index 351d2a58f7..34f4046b38 100644 --- a/rts/FileLock.c +++ b/rts/FileLock.c @@ -25,10 +25,10 @@ typedef struct { // Two hash tables. The first maps objects (device/inode pairs) to // Lock objects containing the number of active readers or writers. The -// second maps file descriptors to lock objects, so that we can unlock -// by FD without needing to fstat() again. +// second maps file descriptors or file handles to lock objects, so that we can +// unlock by FD or HANDLE without needing to fstat() again. static HashTable *obj_hash; -static HashTable *fd_hash; +static HashTable *key_hash; #if defined(THREADED_RTS) static Mutex file_lock_mutex; @@ -53,7 +53,7 @@ void initFileLocking(void) { obj_hash = allocHashTable(); - fd_hash = allocHashTable(); /* ordinary word-based table */ + key_hash = allocHashTable(); /* ordinary word-based table */ #if defined(THREADED_RTS) initMutex(&file_lock_mutex); #endif @@ -69,14 +69,14 @@ void freeFileLocking(void) { freeHashTable(obj_hash, freeLock); - freeHashTable(fd_hash, NULL); + freeHashTable(key_hash, NULL); #if defined(THREADED_RTS) closeMutex(&file_lock_mutex); #endif } int -lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) +lockFile(StgWord id, StgWord64 dev, StgWord64 ino, int for_writing) { Lock key, *lock; @@ -94,7 +94,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) lock->inode = ino; lock->readers = for_writing ? -1 : 1; insertHashTable_(obj_hash, (StgWord)lock, (void *)lock, hashLock); - insertHashTable(fd_hash, fd, lock); + insertHashTable(key_hash, id, lock); RELEASE_LOCK(&file_lock_mutex); return 0; } @@ -105,7 +105,7 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) RELEASE_LOCK(&file_lock_mutex); return -1; } - insertHashTable(fd_hash, fd, lock); + insertHashTable(key_hash, id, lock); lock->readers++; RELEASE_LOCK(&file_lock_mutex); return 0; @@ -113,15 +113,15 @@ lockFile(int fd, StgWord64 dev, StgWord64 ino, int for_writing) } int -unlockFile(int fd) +unlockFile(StgWord id) { Lock *lock; ACQUIRE_LOCK(&file_lock_mutex); - lock = lookupHashTable(fd_hash, fd); + lock = lookupHashTable(key_hash, id); if (lock == NULL) { - // errorBelch("unlockFile: fd %d not found", fd); + // errorBelch("unlockFile: key %d not found", key); // This is normal: we didn't know when calling unlockFile // whether this FD referred to a locked file or not. RELEASE_LOCK(&file_lock_mutex); @@ -138,7 +138,7 @@ unlockFile(int fd) removeHashTable_(obj_hash, (StgWord)lock, NULL, hashLock, cmpLocks); stgFree(lock); } - removeHashTable(fd_hash, fd, NULL); + removeHashTable(key_hash, id, NULL); RELEASE_LOCK(&file_lock_mutex); return 0; diff --git a/rts/Prelude.h b/rts/Prelude.h index c6971677af..0527218da0 100644 --- a/rts/Prelude.h +++ b/rts/Prelude.h @@ -52,8 +52,12 @@ PRELUDE_CLOSURE(base_GHCziExceptionziType_overflowException_closure); PRELUDE_CLOSURE(base_GHCziConcziSync_runSparks_closure); PRELUDE_CLOSURE(base_GHCziConcziIO_ensureIOManagerIsRunning_closure); +PRELUDE_CLOSURE(base_GHCziConcziIO_interruptIOManager_closure); PRELUDE_CLOSURE(base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure); PRELUDE_CLOSURE(base_GHCziConcziSignal_runHandlersPtr_closure); +#if defined(mingw32_HOST_OS) +PRELUDE_CLOSURE(base_GHCziEventziWindows_processRemoteCompletion_closure); +#endif PRELUDE_CLOSURE(base_GHCziTopHandler_flushStdHandles_closure); PRELUDE_CLOSURE(base_GHCziTopHandler_runMainIO_closure); @@ -85,8 +89,12 @@ PRELUDE_INFO(base_GHCziStable_StablePtr_con_info); #define runSparks_closure DLL_IMPORT_DATA_REF(base_GHCziConcziSync_runSparks_closure) #define ensureIOManagerIsRunning_closure DLL_IMPORT_DATA_REF(base_GHCziConcziIO_ensureIOManagerIsRunning_closure) +#define interruptIOManager_closure DLL_IMPORT_DATA_REF(base_GHCziConcziIO_interruptIOManager_closure) #define ioManagerCapabilitiesChanged_closure DLL_IMPORT_DATA_REF(base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure) #define runHandlersPtr_closure DLL_IMPORT_DATA_REF(base_GHCziConcziSignal_runHandlersPtr_closure) +#if defined(mingw32_HOST_OS) +#define processRemoteCompletion_closure DLL_IMPORT_DATA_REF(base_GHCziEventziWindows_processRemoteCompletion_closure) +#endif #define flushStdHandles_closure DLL_IMPORT_DATA_REF(base_GHCziTopHandler_flushStdHandles_closure) #define runMainIO_closure DLL_IMPORT_DATA_REF(base_GHCziTopHandler_runMainIO_closure) diff --git a/rts/RtsStartup.c b/rts/RtsStartup.c index 31f8267708..a52c02190e 100644 --- a/rts/RtsStartup.c +++ b/rts/RtsStartup.c @@ -46,7 +46,8 @@ #endif #if defined(mingw32_HOST_OS) && !defined(THREADED_RTS) -#include "win32/AsyncIO.h" +#include "win32/AsyncMIO.h" +#include "win32/AsyncWinIO.h" #endif #if defined(mingw32_HOST_OS) @@ -299,10 +300,13 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config) getStablePtr((StgPtr)nestedAtomically_closure); getStablePtr((StgPtr)runSparks_closure); getStablePtr((StgPtr)ensureIOManagerIsRunning_closure); + getStablePtr((StgPtr)interruptIOManager_closure); getStablePtr((StgPtr)ioManagerCapabilitiesChanged_closure); #if !defined(mingw32_HOST_OS) getStablePtr((StgPtr)blockedOnBadFD_closure); getStablePtr((StgPtr)runHandlersPtr_closure); +#else + getStablePtr((StgPtr)processRemoteCompletion_closure); #endif // Initialize the top-level handler system @@ -338,7 +342,10 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config) #endif #if defined(mingw32_HOST_OS) && !defined(THREADED_RTS) - startupAsyncIO(); + if (is_io_mng_native_p()) + startupAsyncWinIO(); + else + startupAsyncIO(); #endif x86_init_fpu(); @@ -520,7 +527,10 @@ hs_exit_(bool wait_foreign) #endif #if defined(mingw32_HOST_OS) && !defined(THREADED_RTS) - shutdownAsyncIO(wait_foreign); + if (is_io_mng_native_p()) + shutdownAsyncWinIO(wait_foreign); + else + shutdownAsyncIO(wait_foreign); #endif /* free hash table storage */ diff --git a/rts/RtsSymbols.c b/rts/RtsSymbols.c index 0f0bd56c82..ff430d0137 100644 --- a/rts/RtsSymbols.c +++ b/rts/RtsSymbols.c @@ -26,6 +26,7 @@ #include <io.h> #include <windows.h> #include <shfolder.h> /* SHGetFolderPathW */ +#include "win32/AsyncWinIO.h" #endif #if defined(openbsd_HOST_OS) @@ -142,11 +143,15 @@ /* see Note [Symbols for MinGW's printf] */ \ SymI_HasProto(_lock_file) \ SymI_HasProto(_unlock_file) \ + SymI_HasProto(__mingw_vsnwprintf) \ + /* ^^ Need to figure out why this is needed. */ \ /* See Note [_iob_func symbol] */ \ RTS_WIN64_ONLY(SymI_HasProto_redirect( \ __imp___acrt_iob_func, __rts_iob_func, true)) \ RTS_WIN32_ONLY(SymI_HasProto_redirect( \ - __imp____acrt_iob_func, __rts_iob_func, true)) + __imp____acrt_iob_func, __rts_iob_func, true)) \ + SymI_HasProto(__mingw_vsnwprintf) + /* ^^ Need to figure out why this is needed. */ #define RTS_MINGW_COMPAT_SYMBOLS \ SymI_HasProto_deprecated(access) \ @@ -337,11 +342,16 @@ SymI_HasProto(blockUserSignals) \ SymI_HasProto(unblockUserSignals) #else -#define RTS_USER_SIGNALS_SYMBOLS \ - SymI_HasProto(ioManagerWakeup) \ - SymI_HasProto(sendIOManagerEvent) \ - SymI_HasProto(readIOManagerEvent) \ - SymI_HasProto(getIOManagerEvent) \ +#define RTS_USER_SIGNALS_SYMBOLS \ + SymI_HasProto(registerNewIOCPHandle) \ + SymI_HasProto(getOverlappedEntries) \ + SymI_HasProto(servicedIOEntries) \ + SymI_HasProto(completeSynchronousRequest) \ + SymI_HasProto(registerAlertableWait) \ + SymI_HasProto(ioManagerWakeup) \ + SymI_HasProto(sendIOManagerEvent) \ + SymI_HasProto(readIOManagerEvent) \ + SymI_HasProto(getIOManagerEvent) \ SymI_HasProto(console_handler) #endif diff --git a/rts/package.conf.in b/rts/package.conf.in index 45866a1ecd..a03eff6a14 100644 --- a/rts/package.conf.in +++ b/rts/package.conf.in @@ -105,8 +105,12 @@ ld-options: , "-Wl,-u,_base_GHCziExceptionziType_overflowException_closure" , "-Wl,-u,_base_GHCziConcziSync_runSparks_closure" , "-Wl,-u,_base_GHCziConcziIO_ensureIOManagerIsRunning_closure" + , "-Wl,-u,_base_GHCziConcziIO_interruptIOManager_closure" , "-Wl,-u,_base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure" , "-Wl,-u,_base_GHCziConcziSignal_runHandlersPtr_closure" +#if defined(mingw32_HOST_OS) + , "-Wl,-u,_base_GHCziEventziWindows_processRemoteCompletion_closure" +#endif , "-Wl,-u,_base_GHCziTopHandler_flushStdHandles_closure" , "-Wl,-u,_base_GHCziTopHandler_runMainIO_closure" , "-Wl,-u,_ghczmprim_GHCziTypes_Czh_con_info" @@ -214,8 +218,12 @@ ld-options: , "-Wl,-u,base_GHCziExceptionziType_overflowException_closure" , "-Wl,-u,base_GHCziConcziSync_runSparks_closure" , "-Wl,-u,base_GHCziConcziIO_ensureIOManagerIsRunning_closure" + , "-Wl,-u,base_GHCziConcziIO_interruptIOManager_closure" , "-Wl,-u,base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure" , "-Wl,-u,base_GHCziConcziSignal_runHandlersPtr_closure" +#if defined(mingw32_HOST_OS) + , "-Wl,-u,base_GHCziEventziWindows_processRemoteCompletion_closure" +#endif , "-Wl,-u,base_GHCziTopHandler_flushStdHandles_closure" , "-Wl,-u,base_GHCziTopHandler_runMainIO_closure" , "-Wl,-u,ghczmprim_GHCziTypes_Czh_con_info" diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in index ebbd9a9e71..a16582a7f5 100644 --- a/rts/rts.cabal.in +++ b/rts/rts.cabal.in @@ -97,6 +97,7 @@ library dbghelp -- for process information psapi + -- TODO: Hadrian will use this cabal file, so drop WINVER from Hadrian's configs. -- Minimum supported Windows version. -- These numbers can be found at: -- https://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx @@ -231,6 +232,7 @@ library "-Wl,-u,_base_GHCziEventziThread_blockedOnBadFD_closure" "-Wl,-u,_base_GHCziConcziSync_runSparks_closure" "-Wl,-u,_base_GHCziConcziIO_ensureIOManagerIsRunning_closure" + "-Wl,-u,_base_GHCziConcziIO_interruptIOManager_closure" "-Wl,-u,_base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure" "-Wl,-u,_base_GHCziConcziSignal_runHandlersPtr_closure" "-Wl,-u,_base_GHCziTopHandler_flushStdHandles_closure" @@ -310,6 +312,7 @@ library "-Wl,-u,base_GHCziEventziThread_blockedOnBadFD_closure" "-Wl,-u,base_GHCziConcziSync_runSparks_closure" "-Wl,-u,base_GHCziConcziIO_ensureIOManagerIsRunning_closure" + "-Wl,-u,base_GHCziConcziIO_interruptIOManager_closure" "-Wl,-u,base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure" "-Wl,-u,base_GHCziConcziSignal_runHandlersPtr_closure" "-Wl,-u,base_GHCziTopHandler_flushStdHandles_closure" @@ -366,6 +369,17 @@ library -- This symbol is useful in gdb, but not referred to anywhere, -- so we need to force it to be included in the binary. ld-options: "-Wl,-u,findPtr" + -- This symbol is useful in gdb, but not referred to anywhere, + -- so we need to force it to be included in the binary. + "-Wl,-u,findPtr" + + if os(windows) + if flag(leading-underscore) + ld-options: + "-Wl,-u,_base_GHCziEventziWindows_processRemoteCompletion_closure" + else + ld-options: + "-Wl,-u,base_GHCziEventziWindows_processRemoteCompletion_closure" if os(osx) ld-options: "-Wl,-search_paths_first" @@ -497,7 +511,8 @@ library -- I wish we had wildcards..., this would be: -- *.c hooks/**/*.c sm/**/*.c eventlog/**/*.c linker/**/*.c if os(windows) - c-sources: win32/AsyncIO.c + c-sources: win32/AsyncMIO.c + win32/AsyncWinIO.c win32/AwaitEvent.c win32/ConsoleHandler.c win32/GetEnv.c diff --git a/rts/win32/AsyncIO.c b/rts/win32/AsyncIO.c deleted file mode 100644 index 49da79d2dd..0000000000 --- a/rts/win32/AsyncIO.c +++ /dev/null @@ -1,384 +0,0 @@ -/* AsyncIO.c - * - * Integrating Win32 asynchronous I/O with the GHC RTS. - * - * (c) sof, 2002-2003. - */ - -#if !defined(THREADED_RTS) - -#include "Rts.h" -#include "RtsUtils.h" -#include <windows.h> -#include <stdio.h> -#include "Schedule.h" -#include "Capability.h" -#include "win32/AsyncIO.h" -#include "win32/IOManager.h" - -/* - * Overview: - * - * Haskell code issue asynchronous I/O requests via the - * async{Read,Write,DoOp}# primops. These cause addIORequest() - * to be invoked, which forwards the request to the underlying - * asynchronous I/O subsystem. Each request is tagged with a unique - * ID. - * - * addIORequest() returns this ID, so that when the blocked CH - * thread is added onto blocked_queue, its TSO is annotated with - * it. Upon completion of an I/O request, the async I/O handling - * code makes a back-call to signal its completion; the local - * onIOComplete() routine. It adds the IO request ID (along with - * its result data) to a queue of completed requests before returning. - * - * The queue of completed IO request is read by the thread operating - * the RTS scheduler. It de-queues the CH threads corresponding - * to the request IDs, making them runnable again. - * - */ - -typedef struct CompletedReq { - unsigned int reqID; - HsInt len; - HsInt errCode; -} CompletedReq; - -#define MAX_REQUESTS 200 - -static CRITICAL_SECTION queue_lock; -static HANDLE completed_req_event = INVALID_HANDLE_VALUE; -static HANDLE abandon_req_wait = INVALID_HANDLE_VALUE; -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 -onIOComplete(unsigned int reqID, - int fd STG_UNUSED, - HsInt len, - void* buf STG_UNUSED, - HsInt errCode) -{ - 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: - case WAIT_ABANDONED: - break; - default: - /* Not likely */ - fprintf(stderr, - "onIOComplete: failed to grab table semaphore (res=%d, err=%d), " - "dropping request 0x%x\n", reqID, dwRes, GetLastError()); - fflush(stderr); - return; - } - EnterCriticalSection(&queue_lock); - if (completed_hw == MAX_REQUESTS) { - /* Shouldn't happen */ - fprintf(stderr, "onIOComplete: ERROR -- Request table overflow (%d); " - "dropping.\n", reqID); - fflush(stderr); - } else { -#if 0 - fprintf(stderr, "onCompl: %d %d %d %d %d\n", - reqID, len, errCode, issued_reqs, completed_hw); - fflush(stderr); -#endif - completedTable[completed_hw].reqID = reqID; - completedTable[completed_hw].len = len; - completedTable[completed_hw].errCode = errCode; - completed_hw++; - 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. The event resets - * once that thread has cleared out the request queue/table. - */ - SetEvent(completed_req_event); - } - } - LeaveCriticalSection(&queue_lock); -} - -unsigned int -addIORequest(int fd, - bool forWriting, - bool isSock, - HsInt len, - char* buf) -{ - EnterCriticalSection(&queue_lock); - issued_reqs++; - LeaveCriticalSection(&queue_lock); -#if 0 - fprintf(stderr, "addIOReq: %d %d %d\n", fd, forWriting, len); - fflush(stderr); -#endif - return AddIORequest(fd,forWriting,isSock,len,buf,onIOComplete); -} - -unsigned int -addDelayRequest(HsInt usecs) -{ - EnterCriticalSection(&queue_lock); - issued_reqs++; - LeaveCriticalSection(&queue_lock); -#if 0 - fprintf(stderr, "addDelayReq: %d\n", usecs); fflush(stderr); -#endif - return AddDelayRequest(usecs,onIOComplete); -} - -unsigned int -addDoProcRequest(void* proc, void* param) -{ - EnterCriticalSection(&queue_lock); - issued_reqs++; - LeaveCriticalSection(&queue_lock); -#if 0 - fprintf(stderr, "addProcReq: %p %p\n", proc, param); fflush(stderr); -#endif - return AddProcRequest(proc,param,onIOComplete); -} - - -int -startupAsyncIO() -{ - if (!StartIOManager()) { - return 0; - } - InitializeCriticalSection(&queue_lock); - /* Create a pair of events: - * - * - completed_req_event -- signals the deposit of request result; - * manual reset. - * - abandon_req_wait -- external OS thread tells current - * RTS/Scheduler thread to abandon wait - * for IO request completion. - * Auto reset. - */ - completed_req_event = CreateEvent (NULL, TRUE, FALSE, NULL); - abandon_req_wait = CreateEvent (NULL, FALSE, FALSE, NULL); - wait_handles[0] = completed_req_event; - wait_handles[1] = abandon_req_wait; - completed_hw = 0; - if ( !(completed_table_sema = CreateSemaphore(NULL, MAX_REQUESTS, - MAX_REQUESTS, NULL)) ) { - DWORD rc = GetLastError(); - fprintf(stderr, "startupAsyncIO: CreateSemaphore failed 0x%x\n", - (int)rc); - fflush(stderr); - } - - return ( completed_req_event != INVALID_HANDLE_VALUE && - abandon_req_wait != INVALID_HANDLE_VALUE && - completed_table_sema != NULL ); -} - -void -shutdownAsyncIO(bool wait_threads) -{ - ShutdownIOManager(wait_threads); - if (completed_req_event != INVALID_HANDLE_VALUE) { - CloseHandle(completed_req_event); - completed_req_event = INVALID_HANDLE_VALUE; - } - if (abandon_req_wait != INVALID_HANDLE_VALUE) { - CloseHandle(abandon_req_wait); - abandon_req_wait = INVALID_HANDLE_VALUE; - } - if (completed_table_sema != NULL) { - CloseHandle(completed_table_sema); - completed_table_sema = NULL; - } - DeleteCriticalSection(&queue_lock); -} - -/* - * Function: awaitRequests(wait) - * - * Check for the completion of external IO work requests. Worker - * threads signal completion of IO requests by depositing them - * in a table (completedTable). awaitRequests() matches up - * requests in that table with threads on the blocked_queue, - * making the threads whose IO requests have completed runnable - * again. - * - * awaitRequests() is called by the scheduler periodically _or_ if - * it is out of work, and need to wait for the completion of IO - * requests to make further progress. In the latter scenario, - * awaitRequests() will simply block waiting for worker threads - * to complete if the 'completedTable' is empty. - */ -int -awaitRequests(bool wait) -{ -#if !defined(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); - fflush(stderr); -#endif - EnterCriticalSection(&queue_lock); - // Nothing immediately available & we won't wait - if ((!wait && completed_hw == 0) -#if 0 - // If we just return when wait==false, 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; - } - if (completed_hw == 0) { - // empty table, drop lock and wait - LeaveCriticalSection(&queue_lock); - if ( wait && sched_state == SCHED_RUNNING ) { - 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; - } - goto start; - } else { - int i; - StgTSO *tso, *prev; - - for (i=0; i < completed_hw; i++) { - /* For each of the completed requests, match up their Ids - * with those of the threads on the blocked_queue. If the - * thread that made the IO request has been subsequently - * killed (and removed from blocked_queue), no match will - * be found for that request Id. - * - * i.e., killing a Haskell thread doesn't attempt to cancel - * the IO request it is blocked on. - * - */ - unsigned int rID = completedTable[i].reqID; - - prev = NULL; - for(tso = blocked_queue_hd; tso != END_TSO_QUEUE; - tso = tso->_link) { - - switch(tso->why_blocked) { - case BlockedOnRead: - case BlockedOnWrite: - case BlockedOnDoProc: - if (tso->block_info.async_result->reqID == rID) { - // Found the thread blocked waiting on request; - // stodgily fill - // in its result block. - tso->block_info.async_result->len = - completedTable[i].len; - tso->block_info.async_result->errCode = - completedTable[i].errCode; - - // Drop the matched TSO from blocked_queue - if (prev) { - setTSOLink(&MainCapability, prev, tso->_link); - } else { - blocked_queue_hd = tso->_link; - } - if (blocked_queue_tl == tso) { - blocked_queue_tl = prev ? prev : END_TSO_QUEUE; - } - - // Terminates the run queue + this inner for-loop. - tso->_link = END_TSO_QUEUE; - tso->why_blocked = NotBlocked; - // save the StgAsyncIOResult in the - // stg_block_async_info stack frame, because - // the block_info field will be overwritten by - // pushOnRunQueue(). - tso->stackobj->sp[1] = (W_)tso->block_info.async_result; - pushOnRunQueue(&MainCapability, tso); - break; - } - break; - default: - if (tso->why_blocked != NotBlocked) { - barf("awaitRequests: odd thread state"); - } - break; - } - - prev = tso; - } - /* 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", (int)dw); - fflush(stderr); - } - } - completed_hw = 0; - ResetEvent(completed_req_event); - LeaveCriticalSection(&queue_lock); - return 1; - } -#endif /* !THREADED_RTS */ -} - -/* - * Function: abandonRequestWait() - * - * Wake up a thread that's blocked waiting for new IO requests - * to complete (via awaitRequests().) - */ -void -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 - */ - SetEvent(abandon_req_wait); -} - -void -resetAbandonRequestWait( void ) -{ - ResetEvent(abandon_req_wait); -} - -#endif /* !defined(THREADED_RTS) */ diff --git a/rts/win32/AsyncIO.h b/rts/win32/AsyncIO.h deleted file mode 100644 index 75d0e0460d..0000000000 --- a/rts/win32/AsyncIO.h +++ /dev/null @@ -1,24 +0,0 @@ -/* AsyncIO.h - * - * Integrating Win32 asynchronous I/O with the GHC RTS. - * - * (c) sof, 2002-2003. - */ - -#pragma once - -extern unsigned int -addIORequest(int fd, - bool forWriting, - bool isSock, - HsInt len, - char* buf); -extern unsigned int addDelayRequest(HsInt usecs); -extern unsigned int addDoProcRequest(void* proc, void* param); -extern int startupAsyncIO(void); -extern void shutdownAsyncIO(bool wait_threads); - -extern int awaitRequests(bool wait); - -extern void abandonRequestWait(void); -extern void resetAbandonRequestWait(void); diff --git a/rts/win32/AwaitEvent.c b/rts/win32/AwaitEvent.c index b639121c87..6a621d6ef5 100644 --- a/rts/win32/AwaitEvent.c +++ b/rts/win32/AwaitEvent.c @@ -14,15 +14,18 @@ * */ #include "Rts.h" +#include "RtsFlags.h" #include "Schedule.h" #include "AwaitEvent.h" #include <windows.h> -#include "win32/AsyncIO.h" +#include "win32/AsyncMIO.h" +#include "win32/AsyncWinIO.h" #include "win32/ConsoleHandler.h" +#include <stdbool.h> // Used to avoid calling abandonRequestWait() if we don't need to. // Protected by sched_mutex. -static uint32_t workerWaitingForRequests = 0; +static bool workerWaitingForRequests = false; void awaitEvent(bool wait) @@ -30,9 +33,12 @@ awaitEvent(bool wait) do { /* Try to de-queue completed IO requests */ - workerWaitingForRequests = 1; - awaitRequests(wait); - workerWaitingForRequests = 0; + workerWaitingForRequests = true; + if (is_io_mng_native_p()) + awaitAsyncRequests(wait); + else + awaitRequests(wait); + workerWaitingForRequests = false; // If a signal was raised, we need to service it // XXX the scheduler loop really should be calling diff --git a/rts/win32/ConsoleHandler.c b/rts/win32/ConsoleHandler.c index 3ddf4103da..05d15868eb 100644 --- a/rts/win32/ConsoleHandler.c +++ b/rts/win32/ConsoleHandler.c @@ -1,13 +1,15 @@ /* * Console control handler support. * + * NOTE: This is the MIO manager, only used for --io-manager=posix. + * For the WINIO manager see base in the GHC.Event modules. */ #include "Rts.h" #include <windows.h> #include "ConsoleHandler.h" #include "Schedule.h" #include "RtsUtils.h" -#include "AsyncIO.h" +#include "AsyncMIO.h" #include "RtsSignals.h" extern int stg_InstallConsoleEvent(int action, StgStablePtr *handler); @@ -86,7 +88,6 @@ static BOOL WINAPI shutdown_handler(DWORD dwCtrlType) return false; case CTRL_C_EVENT: case CTRL_BREAK_EVENT: - // If we're already trying to interrupt the RTS, terminate with // extreme prejudice. So the first ^C tries to exit the program // cleanly, and the second one just kills it. @@ -223,12 +224,12 @@ static BOOL WINAPI generic_handler(DWORD dwCtrlType) #if defined(THREADED_RTS) sendIOManagerEvent((StgWord8) ((dwCtrlType<<1) | 1)); + interruptIOManagerEvent (); #else if ( stg_pending_events < N_PENDING_EVENTS ) { stg_pending_buf[stg_pending_events] = dwCtrlType; stg_pending_events++; } - // we need to wake up awaitEvent() abandonRequestWait(); #endif diff --git a/rts/win32/ConsoleHandler.h b/rts/win32/ConsoleHandler.h index 06af9dd0d0..bb7278abba 100644 --- a/rts/win32/ConsoleHandler.h +++ b/rts/win32/ConsoleHandler.h @@ -1,6 +1,8 @@ /* * Console control handler support. * + * NOTE: This is the MIO manager, only used for --io-manager=posix. + * For the WINIO manager see base in the GHC.Event modules. */ #pragma once @@ -16,24 +18,24 @@ */ #if !defined(THREADED_RTS) -/* +/* * under THREADED_RTS, console events are passed to the IO manager * thread, which starts up the handler. See ThrIOManager.c. */ /* - * Function: signals_pending() - * + * Function: signals_pending() + * * Used by the RTS to check whether new signals have been 'recently' reported. - * If so, the RTS arranges for the delivered signals to be handled by - * de-queueing them from their table, running the associated Haskell + * If so, the RTS arranges for the delivered signals to be handled by + * de-queueing them from their table, running the associated Haskell * signal handler. */ extern StgInt stg_pending_events; #define signals_pending() ( stg_pending_events > 0) -/* +/* * Function: anyUserHandlers() * * Used by the Scheduler to decide whether its worth its while to stick diff --git a/rts/win32/IOManager.c b/rts/win32/IOManager.c index e5da32b982..47bcf4bcf4 100644 --- a/rts/win32/IOManager.c +++ b/rts/win32/IOManager.c @@ -3,6 +3,9 @@ * Non-blocking / asynchronous I/O for Win32. * * (c) sof, 2002-2003. + * + * NOTE: This is the MIO manager, only used for --io-manager=posix. + * For the WINIO manager see base in the GHC.Event modules. */ #if !defined(THREADED_RTS) diff --git a/rts/win32/IOManager.h b/rts/win32/IOManager.h index a5bd61ab1b..cb876db9cc 100644 --- a/rts/win32/IOManager.h +++ b/rts/win32/IOManager.h @@ -3,6 +3,9 @@ * Non-blocking / asynchronous I/O for Win32. * * (c) sof, 2002-2003 + * + * NOTE: This is the MIO manager, only used for --io-manager=posix. + * For the WINIO manager see base in the GHC.Event modules. */ #pragma once @@ -102,3 +105,5 @@ extern int AddProcRequest ( void* proc, CompletionProc onCompletion); extern void abandonWorkRequest ( int reqID ); + +extern void interruptIOManagerEvent ( void ); diff --git a/rts/win32/ThrIOManager.c b/rts/win32/ThrIOManager.c index 44414b92c3..b70a178faf 100644 --- a/rts/win32/ThrIOManager.c +++ b/rts/win32/ThrIOManager.c @@ -5,6 +5,7 @@ * The IO manager thread in THREADED_RTS. * See also libraries/base/GHC/Conc.hs. * + * NOTE: This is used by both MIO and WINIO * ---------------------------------------------------------------------------*/ #include "Rts.h" @@ -15,28 +16,14 @@ // Here's the Event that we use to wake up the IO manager thread static HANDLE io_manager_event = INVALID_HANDLE_VALUE; -// must agree with values in GHC.Conc: -#define IO_MANAGER_WAKEUP 0xffffffff -#define IO_MANAGER_DIE 0xfffffffe -// spurious wakeups are returned as zero. -// console events are ((event<<1) | 1) - -#if defined(THREADED_RTS) - #define EVENT_BUFSIZ 256 Mutex event_buf_mutex; StgWord32 event_buf[EVENT_BUFSIZ]; uint32_t next_event; -#endif - HANDLE getIOManagerEvent (void) { - // This function has to exist even in the non-THREADED_RTS, - // because code in GHC.Conc refers to it. It won't ever be called - // unless we're in the threaded RTS, however. -#if defined(THREADED_RTS) HANDLE hRes; ACQUIRE_LOCK(&event_buf_mutex); @@ -57,18 +44,12 @@ getIOManagerEvent (void) RELEASE_LOCK(&event_buf_mutex); return hRes; -#else - return NULL; -#endif } HsWord32 readIOManagerEvent (void) { - // This function must exist even in non-THREADED_RTS, - // see getIOManagerEvent() above. -#if defined(THREADED_RTS) HsWord32 res; ACQUIRE_LOCK(&event_buf_mutex); @@ -77,7 +58,11 @@ readIOManagerEvent (void) if (next_event == 0) { res = 0; // no event to return } else { - res = (HsWord32)(event_buf[--next_event]); + do { + // Dequeue as many wakeup events as possible. + res = (HsWord32)(event_buf[--next_event]); + } while (res == IO_MANAGER_WAKEUP && next_event); + if (next_event == 0) { if (!ResetEvent(io_manager_event)) { sysErrorBelch("readIOManagerEvent"); @@ -91,34 +76,45 @@ readIOManagerEvent (void) RELEASE_LOCK(&event_buf_mutex); - // debugBelch("readIOManagerEvent: %d\n", res); + //debugBelch("readIOManagerEvent: %d\n", res); return res; -#else - return 0; -#endif } void sendIOManagerEvent (HsWord32 event) { -#if defined(THREADED_RTS) ACQUIRE_LOCK(&event_buf_mutex); - // debugBelch("sendIOManagerEvent: %d\n", event); + //debugBelch("sendIOManagerEvent: %d to %p\n", event, io_manager_event); if (io_manager_event != INVALID_HANDLE_VALUE) { if (next_event == EVENT_BUFSIZ) { errorBelch("event buffer overflowed; event dropped"); } else { + event_buf[next_event++] = (StgWord32)event; if (!SetEvent(io_manager_event)) { - sysErrorBelch("sendIOManagerEvent"); + sysErrorBelch("sendIOManagerEvent: SetEvent"); stg_exit(EXIT_FAILURE); } - event_buf[next_event++] = (StgWord32)event; } } RELEASE_LOCK(&event_buf_mutex); -#endif +} + +void +interruptIOManagerEvent (void) +{ + if (is_io_mng_native_p ()) { + ACQUIRE_LOCK(&event_buf_mutex); + + /* How expensive is this??. */ + Capability *cap; + cap = rts_lock(); + rts_evalIO(&cap, interruptIOManager_closure, NULL); + rts_unlock(cap); + + RELEASE_LOCK(&event_buf_mutex); + } } void @@ -127,7 +123,6 @@ ioManagerWakeup (void) sendIOManagerEvent(IO_MANAGER_WAKEUP); } -#if defined(THREADED_RTS) void ioManagerDie (void) { @@ -145,7 +140,9 @@ ioManagerDie (void) void ioManagerStart (void) { +#if defined(THREADED_RTS) initMutex(&event_buf_mutex); +#endif next_event = 0; // Make sure the IO manager thread is running @@ -156,4 +153,3 @@ ioManagerStart (void) rts_unlock(cap); } } -#endif diff --git a/rts/win32/libHSbase.def b/rts/win32/libHSbase.def index de4db2244b..fb705bbd9f 100644 --- a/rts/win32/libHSbase.def +++ b/rts/win32/libHSbase.def @@ -27,8 +27,10 @@ EXPORTS base_GHCziPtr_FunPtr_con_info base_GHCziConcziIO_ensureIOManagerIsRunning_closure + base_GHCziConcziIO_interruptIOManager_closure base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure base_GHCziConcziSync_runSparks_closure + base_GHCziEventziWindows_processRemoteCompletion_closure base_GHCziTopHandler_flushStdHandles_closure |