两个版本的链接:
https://github.com/neosmart/pevents
https://github.com/moya-lang/Event
第一个版本能够模拟等待多个事件中的一个触发,而后者仅最多支持一个事件
但第一个版本在UnlockedWaitForEvent执行后,是需要增加一个判断的,否则会不正确
代码:
#if 0 //#define PULSE //#define WFMO #include <assert.h> #include <errno.h> #include <pthread.h> #include <sys/time.h> #ifdef WFMO #include <algorithm> #include <deque> #endif struct SyncObjectPosix_event_st; // Function declarations void* CreateEvent(void* lpEventAttributes = nullptr, bool manualReset = false, bool initialState = false, void* lpName = nullptr); int DestroyEvent(void* event); //int WaitForEvent(void* event, uint64_t milliseconds = -1); int WaitForSingleObject(void* event, uint64_t milliseconds = -1); int SetEvent(void* event); int ResetEvent(void* event); #ifdef WFMO int WaitForMultipleObjects(int nCount,const void* *lpHandles, bool bWaitAll, uint64_t milliseconds); int WaitForMultipleEvents(void* *events, int count, bool waitAll, uint64_t milliseconds); int WaitForMultipleEvents(void* *events, int count, bool waitAll, uint64_t milliseconds, int &index); #endif #ifdef PULSE int PulseEvent(void* event); #endif #ifdef WFMO // Each call to WaitForMultipleObjects initializes a neosmart_wfmo_t object which tracks // the progress of the caller's multi-object wait and dispatches responses accordingly. // One neosmart_wfmo_t struct is shared for all events in a single WFMO call typedef struct SyncObjectPosix_wfmo_st { pthread_mutex_t Mutex; pthread_cond_t CVariable; int RefCount; union { int FiredEvent; // WFSO int EventsLeft; // WFMO } Status; bool WaitAll; bool StillWaiting; void Destroy() { pthread_mutex_destroy(&Mutex); pthread_cond_destroy(&CVariable); } }SyncObjectPosix_wfmo_st; //typedef SyncObjectPosix_wfmo_t_ *SyncObjectPosix_wfmo_t; // A neosmart_wfmo_info_t object is registered with each event waited on in a WFMO // This reference to neosmart_wfmo_t_ is how the event knows whom to notify when triggered typedef struct SyncObjectPosix_wfmo_info_st { SyncObjectPosix_wfmo_st* Waiter; int WaitIndex; }SyncObjectPosix_wfmo_info_st; //typedef SyncObjectPosix_wfmo_info_t_ *nSyncObjectPosix_wfmo_info_t; #endif // WFMO // The basic event structure, passed to the caller as an opaque pointer when creating events typedef struct SyncObjectPosix_event_st { pthread_cond_t CVariable; pthread_mutex_t Mutex; bool AutoReset; bool State; #ifdef WFMO std::deque<SyncObjectPosix_wfmo_info_st> RegisteredWaits; #endif }SyncObjectPosix_event_st; #ifdef WFMO bool RemoveExpiredWaitHelper(SyncObjectPosix_wfmo_info_st wait) { int result = pthread_mutex_trylock(&wait.Waiter->Mutex); if (result == EBUSY) { return false; } assert(result == 0); if (wait.Waiter->StillWaiting == false) { --wait.Waiter->RefCount; assert(wait.Waiter->RefCount >= 0); bool destroy = wait.Waiter->RefCount == 0; result = pthread_mutex_unlock(&wait.Waiter->Mutex); assert(result == 0); if (destroy) { wait.Waiter->Destroy(); delete wait.Waiter; } return true; } result = pthread_mutex_unlock(&wait.Waiter->Mutex); assert(result == 0); return false; } #endif // WFMO void* CreateEvent(void* lpEventAttributes, bool manualReset, bool initialState, void* lpName) { SyncObjectPosix_event_st* event = new SyncObjectPosix_event_st; int result = pthread_cond_init(&event->CVariable, 0); assert(result == 0); result = pthread_mutex_init(&event->Mutex, 0); assert(result == 0); event->State = false; event->AutoReset = !manualReset; if (initialState) { result = SetEvent(event); assert(result == 0); } return event; } int UnlockedWaitForEvent(void* event, uint64_t milliseconds) { int result = 0; if (!((SyncObjectPosix_event_st*)event)->State) { // Zero-timeout event state check optimization if (milliseconds == 0) { return WAIT_TIMEOUT; } timespec ts; if (milliseconds != (uint64_t)-1) { timeval tv; gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t)tv.tv_sec) * 1000 * 1000 * 1000 + milliseconds * 1000 * 1000 + ((uint64_t)tv.tv_usec) * 1000; ts.tv_sec = nanoseconds / 1000 / 1000 / 1000; ts.tv_nsec = (nanoseconds - ((uint64_t)ts.tv_sec) * 1000 * 1000 * 1000); } do { // Regardless of whether it's an auto-reset or manual-reset event: // wait to obtain the event, then lock anyone else out if (milliseconds != (uint64_t)-1) { result = pthread_cond_timedwait(&((SyncObjectPosix_event_st*)event)->CVariable, &((SyncObjectPosix_event_st*)event)->Mutex, &ts); } else { result = pthread_cond_wait(&((SyncObjectPosix_event_st*)event)->CVariable, &((SyncObjectPosix_event_st*)event)->Mutex); } } while (result == 0 && !((SyncObjectPosix_event_st*)event)->State); if (result == 0 && ((SyncObjectPosix_event_st*)event)->AutoReset) { // We've only accquired the event if the wait succeeded ((SyncObjectPosix_event_st*)event)->State = false; } } else if (((SyncObjectPosix_event_st*)event)->AutoReset) { // It's an auto-reset event that's currently available; // we need to stop anyone else from using it result = 0; ((SyncObjectPosix_event_st*)event)->State = false; } // Else we're trying to obtain a manual reset event with a signaled state; // don't do anything return result; } int WaitForSingleObject(void* event, uint64_t milliseconds) { int tempResult; if (milliseconds == 0) { tempResult = pthread_mutex_trylock(&((SyncObjectPosix_event_st*)event)->Mutex); if (tempResult == EBUSY) { return WAIT_TIMEOUT; } } else { tempResult = pthread_mutex_lock(&((SyncObjectPosix_event_st*)event)->Mutex); } assert(tempResult == 0); int result = UnlockedWaitForEvent(((SyncObjectPosix_event_st*)event), milliseconds); tempResult = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(tempResult == 0); if (result == ETIMEDOUT) { return WAIT_TIMEOUT; } return result; } #ifdef WFMO int WaitForMultipleEvents(void* *events, int count, bool waitAll, uint64_t milliseconds) { int unused; return WaitForMultipleEvents(events, count, waitAll, milliseconds, unused); } int WaitForMultipleEvents(void* *events, int count, bool waitAll, uint64_t milliseconds, int &waitIndex) { SyncObjectPosix_wfmo_st* wfmo = new SyncObjectPosix_wfmo_st; SyncObjectPosix_event_st** pp_events = (SyncObjectPosix_event_st**)events; int result = 0; int tempResult = pthread_mutex_init(&wfmo->Mutex, 0); assert(tempResult == 0); tempResult = pthread_cond_init(&wfmo->CVariable, 0); assert(tempResult == 0); SyncObjectPosix_wfmo_info_st waitInfo; waitInfo.Waiter = wfmo; waitInfo.WaitIndex = -1; wfmo->WaitAll = waitAll; wfmo->StillWaiting = true; wfmo->RefCount = 1; if (waitAll) { wfmo->Status.EventsLeft = count; } else { wfmo->Status.FiredEvent = -1; } tempResult = pthread_mutex_lock(&wfmo->Mutex); assert(tempResult == 0); bool done = false; waitIndex = -1; for (int i = 0; i < count; ++i) { waitInfo.WaitIndex = i; // Must not release lock until RegisteredWait is potentially added tempResult = pthread_mutex_lock(&pp_events[i]->Mutex); assert(tempResult == 0); // Before adding this wait to the list of registered waits, let's clean up old, expired // waits while we have the event lock anyway pp_events[i]->RegisteredWaits.erase(std::remove_if(pp_events[i]->RegisteredWaits.begin(), pp_events[i]->RegisteredWaits.end(), RemoveExpiredWaitHelper), pp_events[i]->RegisteredWaits.end()); if (UnlockedWaitForEvent(events[i], 0) == 0) { tempResult = pthread_mutex_unlock(&pp_events[i]->Mutex); assert(tempResult == 0); if (waitAll) { --wfmo->Status.EventsLeft; assert(wfmo->Status.EventsLeft >= 0); } else { wfmo->Status.FiredEvent = i; waitIndex = i; done = true; break; } } else { pp_events[i]->RegisteredWaits.push_back(waitInfo); ++wfmo->RefCount; tempResult = pthread_mutex_unlock(&pp_events[i]->Mutex); assert(tempResult == 0); } } // We set the `done` flag above in case of WaitAny and at least one event was set. // But we need to check again here if we were doing a WaitAll or else we'll incorrectly // return WAIT_TIMEOUT. if (waitAll && wfmo->Status.EventsLeft == 0) { done = true; } timespec ts; if (!done) { if (milliseconds == 0) { result = WAIT_TIMEOUT; done = true; } else if (milliseconds != (uint64_t)-1) { timeval tv; gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t)tv.tv_sec) * 1000 * 1000 * 1000 + milliseconds * 1000 * 1000 + ((uint64_t)tv.tv_usec) * 1000; ts.tv_sec = nanoseconds / 1000 / 1000 / 1000; ts.tv_nsec = (nanoseconds - ((uint64_t)ts.tv_sec) * 1000 * 1000 * 1000); } } while (!done) { // One (or more) of the events we're monitoring has been triggered? // If we're waiting for all events, assume we're done and check if there's an event that // hasn't fired But if we're waiting for just one event, assume we're not done until we // find a fired event done = (waitAll && wfmo->Status.EventsLeft == 0) || (!waitAll && wfmo->Status.FiredEvent != -1); if (!done) { if (milliseconds != (uint64_t)-1) { result = pthread_cond_timedwait(&wfmo->CVariable, &wfmo->Mutex, &ts); } else { result = pthread_cond_wait(&wfmo->CVariable, &wfmo->Mutex); } if (result != 0) { break; } } } waitIndex = wfmo->Status.FiredEvent; wfmo->StillWaiting = false; --wfmo->RefCount; assert(wfmo->RefCount >= 0); bool destroy = wfmo->RefCount == 0; tempResult = pthread_mutex_unlock(&wfmo->Mutex); assert(tempResult == 0); if (destroy) { wfmo->Destroy(); delete wfmo; } return result; } #endif // WFMO int CloseHandle(void* event) { return DestroyEvent(event); } int DestroyEvent(void* event) { int result = 0; #ifdef WFMO result = pthread_mutex_lock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); ((SyncObjectPosix_event_st*)event)->RegisteredWaits.erase(std::remove_if(((SyncObjectPosix_event_st*)event)->RegisteredWaits.begin(), ((SyncObjectPosix_event_st*)event)->RegisteredWaits.end(), RemoveExpiredWaitHelper), ((SyncObjectPosix_event_st*)event)->RegisteredWaits.end()); result = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); #endif result = pthread_cond_destroy(&((SyncObjectPosix_event_st*)event)->CVariable); assert(result == 0); result = pthread_mutex_destroy(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); delete ((SyncObjectPosix_event_st*)event); return 0; } int SetEvent(void* event) { int result = pthread_mutex_lock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); ((SyncObjectPosix_event_st*)event)->State = true; // Depending on the event type, we either trigger everyone or only one if (((SyncObjectPosix_event_st*)event)->AutoReset) { #ifdef WFMO while (!((SyncObjectPosix_event_st*)event)->RegisteredWaits.empty()) { SyncObjectPosix_wfmo_info_st* i = &((SyncObjectPosix_event_st*)event)->RegisteredWaits.front(); result = pthread_mutex_lock(&i->Waiter->Mutex); assert(result == 0); --i->Waiter->RefCount; assert(i->Waiter->RefCount >= 0); if (!i->Waiter->StillWaiting) { bool destroy = i->Waiter->RefCount == 0; result = pthread_mutex_unlock(&i->Waiter->Mutex); assert(result == 0); if (destroy) { i->Waiter->Destroy(); delete i->Waiter; } ((SyncObjectPosix_event_st*)event)->RegisteredWaits.pop_front(); continue; } ((SyncObjectPosix_event_st*)event)->State = false; if (i->Waiter->WaitAll) { --i->Waiter->Status.EventsLeft; assert(i->Waiter->Status.EventsLeft >= 0); // We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft // != 0 but the only time it'll be equal to zero is if we're the last event, so // no one else will be checking the StillWaiting flag. We're good to go without // it. } else { i->Waiter->Status.FiredEvent = i->WaitIndex; i->Waiter->StillWaiting = false; } result = pthread_mutex_unlock(&i->Waiter->Mutex); assert(result == 0); result = pthread_cond_signal(&i->Waiter->CVariable); assert(result == 0); ((SyncObjectPosix_event_st*)event)->RegisteredWaits.pop_front(); result = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); return 0; } #endif // WFMO // event->State can be false if compiled with WFMO support if (((SyncObjectPosix_event_st*)event)->State) { result = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); result = pthread_cond_signal(&((SyncObjectPosix_event_st*)event)->CVariable); assert(result == 0); return 0; } } else { #ifdef WFMO for (size_t i = 0; i < ((SyncObjectPosix_event_st*)event)->RegisteredWaits.size(); ++i) { SyncObjectPosix_wfmo_info_st* info = &((SyncObjectPosix_event_st*)event)->RegisteredWaits[i]; result = pthread_mutex_lock(&info->Waiter->Mutex); assert(result == 0); --info->Waiter->RefCount; assert(info->Waiter->RefCount >= 0); if (!info->Waiter->StillWaiting) { bool destroy = info->Waiter->RefCount == 0; result = pthread_mutex_unlock(&info->Waiter->Mutex); assert(result == 0); if (destroy) { info->Waiter->Destroy(); delete info->Waiter; } continue; } if (info->Waiter->WaitAll) { --info->Waiter->Status.EventsLeft; assert(info->Waiter->Status.EventsLeft >= 0); // We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft // != 0 but the only time it'll be equal to zero is if we're the last event, so // no one else will be checking the StillWaiting flag. We're good to go without // it. } else { info->Waiter->Status.FiredEvent = info->WaitIndex; info->Waiter->StillWaiting = false; } result = pthread_mutex_unlock(&info->Waiter->Mutex); assert(result == 0); result = pthread_cond_signal(&info->Waiter->CVariable); assert(result == 0); } ((SyncObjectPosix_event_st*)event)->RegisteredWaits.clear(); #endif // WFMO result = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); result = pthread_cond_broadcast(&((SyncObjectPosix_event_st*)event)->CVariable); assert(result == 0); } return 0; } int ResetEvent(void* event) { int result = pthread_mutex_lock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); ((SyncObjectPosix_event_st*)event)->State = false; result = pthread_mutex_unlock(&((SyncObjectPosix_event_st*)event)->Mutex); assert(result == 0); return 0; } #ifdef PULSE int PulseEvent(void* event) { // This may look like it's a horribly inefficient kludge with the sole intention of reducing // code duplication, but in reality this is what any PulseEvent() implementation must look // like. The only overhead (function calls aside, which your compiler will likely optimize // away, anyway), is if only WFMO auto-reset waits are active there will be overhead to // unnecessarily obtain the event mutex for ResetEvent() after. In all other cases (being no // pending waits, WFMO manual-reset waits, or any WFSO waits), the event mutex must first be // released for the waiting thread to resume action prior to locking the mutex again in // order to set the event state to unsignaled, or else the waiting threads will loop back // into a wait (due to checks for spurious CVariable wakeups). int result = SetEvent(event); assert(result == 0); result = ResetEvent(event); assert(result == 0); return 0; } #endif #else #include <mutex> #include <condition_variable> #include <chrono> #include <functional> class SyncObjectPosix { public: SyncObjectPosix(bool initial, bool manual) : state(initial), manual(manual) { } void change(bool manual) { std::unique_lock<std::mutex> lock(mutex); this->manual = manual; } void set() { std::unique_lock<std::mutex> lock(mutex); if (state) return; state = true; if (manual) condition.notify_all(); else condition.notify_one(); } void reset() { std::unique_lock<std::mutex> lock(mutex); state = false; } void wait() { std::unique_lock<std::mutex> lock(mutex); condition.wait(lock, [this] { return state; }); if (!manual) state = false; } template<class Rep, class Period> int wait(const std::chrono::duration<Rep, Period> &timeout) { std::unique_lock<std::mutex> lock(mutex); if (!condition.wait_for(lock, timeout, [this] {return state;} )) { return WAIT_TIMEOUT; } //return; if (!manual) state = false; return 0; } private: bool return_state() { return state; }; std::mutex mutex; std::condition_variable condition; bool state, manual; }; inline void* CreateEvent(void* lpEventAttributes, BOOL bManualReset, BOOL bInitialState, void* lpName){ return (void*)(new SyncObjectPosix(bInitialState, bManualReset)); } inline void CloseHandle(void* p_this) { delete (SyncObjectPosix*)p_this; } inline int WaitForSingleObject(void* p_this, uint64_t milliseconds = -1){ SyncObjectPosix* event_this = (SyncObjectPosix*)p_this; return event_this->wait(std::chrono::milliseconds(milliseconds)); } inline int SetEvent(void* p_this){ SyncObjectPosix* event_this = (SyncObjectPosix*)p_this; event_this->set(); return 0; } inline int ResetEvent(void* p_this){ SyncObjectPosix* event_this = (SyncObjectPosix*)p_this; event_this->reset(); return 0; } #endif