/* ----------------------------------------------------------------------------- * * (c) The GHC Team, 1998-2006 * * 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" #include "ThrIOManager.h" #include "MIOManager.h" #include "rts/OSThreads.h" #include "Prelude.h" #include // Here's the Event that we use to wake up the IO manager thread static HANDLE io_manager_event = INVALID_HANDLE_VALUE; #define EVENT_BUFSIZ 256 // We lock using OS_ACQUIRE_LOCK the ensure the non-threaded WINIO // C thread does not race with the scheduler code which can also // access the event queue via FFI. Mutex event_buf_mutex; StgWord32 event_buf[EVENT_BUFSIZ]; uint32_t next_event; /*Creates the IO Managers event object. Idempotent after first call. */ HANDLE getIOManagerEvent (void) { HANDLE hRes; OS_ACQUIRE_LOCK(&event_buf_mutex); if (io_manager_event == INVALID_HANDLE_VALUE) { hRes = CreateEvent ( NULL, // no security attrs true, // manual reset false, // initial state, NULL ); // event name: NULL for private events if (hRes == NULL) { sysErrorBelch("getIOManagerEvent"); stg_exit(EXIT_FAILURE); } io_manager_event = hRes; } else { hRes = io_manager_event; } OS_RELEASE_LOCK(&event_buf_mutex); return hRes; } HsWord32 readIOManagerEvent (void) { HsWord32 res; OS_ACQUIRE_LOCK(&event_buf_mutex); if (io_manager_event != INVALID_HANDLE_VALUE) { if (next_event == 0) { res = 0; // no event to return } else { 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"); stg_exit(EXIT_FAILURE); } } } } else { res = 0; } OS_RELEASE_LOCK(&event_buf_mutex); //debugBelch("readIOManagerEvent: %d\n", res); return res; } void sendIOManagerEvent (HsWord32 event) { OS_ACQUIRE_LOCK(&event_buf_mutex); //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: SetEvent"); stg_exit(EXIT_FAILURE); } } } OS_RELEASE_LOCK(&event_buf_mutex); } void interruptIOManagerEvent (void) { if (is_io_mng_native_p ()) { OS_ACQUIRE_LOCK(&event_buf_mutex); /* How expensive is this??. */ Capability *cap; cap = rts_lock(); rts_evalIO(&cap, interruptIOManager_closure, NULL); rts_unlock(cap); OS_RELEASE_LOCK(&event_buf_mutex); } } void ioManagerWakeup (void) { sendIOManagerEvent(IO_MANAGER_WAKEUP); } void ioManagerDie (void) { sendIOManagerEvent(IO_MANAGER_DIE); // IO_MANAGER_DIE must be idempotent, as it is called // repeatedly by shutdownCapability(). Try conc059(threaded1) to // illustrate the problem. OS_ACQUIRE_LOCK(&event_buf_mutex); io_manager_event = INVALID_HANDLE_VALUE; OS_RELEASE_LOCK(&event_buf_mutex); // ToDo: wait for the IO manager to pick up the event, and // then release the Event and Mutex objects we've allocated. } void ioManagerStart (void) { #if defined(THREADED_RTS) initMutex(&event_buf_mutex); #endif next_event = 0; // Make sure the IO manager thread is running Capability *cap; if (io_manager_event == INVALID_HANDLE_VALUE) { cap = rts_lock(); rts_evalIO(&cap, ensureIOManagerIsRunning_closure, NULL); rts_unlock(cap); } } /* * Called to close the io_manager_event handle when the IO manager thread is * terminating. */ void ioManagerFinished(void) { CloseHandle(io_manager_event); }