使用pthread下的mutex与cond_var模拟windows下的event几个接口

两个版本的链接:

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
posted @ 2019-09-29 10:25  日月王  阅读(516)  评论(0编辑  收藏  举报