Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
357 lines · 9.6 KB · D
RawDownload
/** * The event module provides a primitive for lightweight signaling of other threads * (emulating Windows events on Posix) * * Copyright: Copyright (c) 2019 D Language Foundation * License: Distributed under the *    $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). *    (See accompanying file LICENSE) * Authors: Rainer Schuetze * Source:    $(DRUNTIMESRC core/sync/event.d) */module core.sync.event; version (Windows){    import core.sys.windows.basetsd /+: HANDLE +/;    import core.sys.windows.winerror /+: WAIT_TIMEOUT +/;    import core.sys.windows.winbase /+: CreateEvent, CloseHandle, SetEvent, ResetEvent,        WaitForSingleObject, INFINITE, WAIT_OBJECT_0+/;}else version (Posix){    import core.sys.posix.pthread : pthread_cond_broadcast, pthread_cond_destroy, pthread_cond_init,        pthread_cond_timedwait, pthread_cond_wait, pthread_mutex_destroy, pthread_mutex_init, pthread_mutex_lock,        pthread_mutex_unlock;    import core.sys.posix.sys.types : pthread_cond_t, pthread_mutex_t;    import core.sys.posix.time : timespec;}else{    static assert(false, "Platform not supported");} import core.internal.abort : abort;import core.time; /** * represents an event. Clients of an event are suspended while waiting * for the event to be "signaled". * * Implemented using `pthread_mutex` and `pthread_condition` on Posix and * `CreateEvent` and `SetEvent` on Windows.---import core.sync.event, core.thread, std.file; struct ProcessFile{    ThreadGroup group;    Event event;    void[] buffer;     void doProcess()    {        event.wait();        // process buffer    }     void process(string filename)    {        event.initialize(true, false);        group = new ThreadGroup;        for (int i = 0; i < 10; ++i)            group.create(&doProcess);         buffer = std.file.read(filename);        event.setIfInitialized();        group.joinAll();        event.terminate();    }}--- */struct Event{nothrow @nogc:    /**     * Creates an event object.     *     * Params:     *  manualReset  = the state of the event is not reset automatically after resuming waiting clients     *  initialState = initial state of the signal     */    this(bool manualReset, bool initialState)    {        initialize(manualReset, initialState);    }     /**     * Initializes an event object. Does nothing if the event is already initialized.     *     * Params:     *  manualReset  = the state of the event is not reset automatically after resuming waiting clients     *  initialState = initial state of the signal     */    void initialize(bool manualReset, bool initialState)    {        version (Windows)        {            if (m_event)                return;            m_event = CreateEvent(null, manualReset, initialState, null);            m_event || abort("Error: CreateEvent failed.");        }        else version (Posix)        {            if (m_initalized)                return;            pthread_mutex_init(cast(pthread_mutex_t*) &m_mutex, null) == 0 ||                abort("Error: pthread_mutex_init failed.");             static if ( is( typeof( imported!"core.sys.posix.pthread".pthread_condattr_setclock ) ) )            {                import core.sys.posix.pthread : CLOCK_MONOTONIC, pthread_condattr_destroy, pthread_condattr_init,                    pthread_condattr_setclock;                import core.sys.posix.sys.types : pthread_condattr_t;                 pthread_condattr_t attr = void;                pthread_condattr_init(&attr) == 0 ||                    abort("Error: pthread_condattr_init failed.");                pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0 ||                    abort("Error: pthread_condattr_setclock failed.");                pthread_cond_init(&m_cond, &attr) == 0 ||                    abort("Error: pthread_cond_init failed.");                pthread_condattr_destroy(&attr) == 0 ||                    abort("Error: pthread_condattr_destroy failed.");            }            else            {                pthread_cond_init(&m_cond, null) == 0 ||                    abort("Error: pthread_cond_init failed.");            }            m_state = initialState;            m_manualReset = manualReset;            m_initalized = true;        }    }     // copying not allowed, can produce resource leaks    @disable this(this);    @disable void opAssign(Event);     ~this()    {        terminate();    }     /**     * deinitialize event. Does nothing if the event is not initialized. There must not be     * threads currently waiting for the event to be signaled.    */    void terminate()    {        version (Windows)        {            if (m_event)                CloseHandle(m_event);            m_event = null;        }        else version (Posix)        {            if (m_initalized)            {                pthread_mutex_destroy(&m_mutex) == 0 ||                    abort("Error: pthread_mutex_destroy failed.");                pthread_cond_destroy(&m_cond) == 0 ||                    abort("Error: pthread_cond_destroy failed.");                m_initalized = false;            }        }    }     deprecated ("Use setIfInitialized() instead") void set()    {        setIfInitialized();    }     /// Set the event to "signaled", so that waiting clients are resumed    void setIfInitialized()    {        version (Windows)        {            if (m_event)                SetEvent(m_event);        }        else version (Posix)        {            if (m_initalized)            {                pthread_mutex_lock(&m_mutex);                m_state = true;                pthread_cond_broadcast(&m_cond);                pthread_mutex_unlock(&m_mutex);            }        }    }     /// Reset the event manually    void reset()    {        version (Windows)        {            if (m_event)                ResetEvent(m_event);        }        else version (Posix)        {            if (m_initalized)            {                pthread_mutex_lock(&m_mutex);                m_state = false;                pthread_mutex_unlock(&m_mutex);            }        }    }     /**     * Wait for the event to be signaled without timeout.     *     * Returns:     *  `true` if the event is in signaled state, `false` if the event is uninitialized or another error occured     */    bool wait()    {        version (Windows)        {            return m_event && WaitForSingleObject(m_event, INFINITE) == WAIT_OBJECT_0;        }        else version (Posix)        {            return wait(Duration.max);        }    }     /**     * Wait for the event to be signaled with timeout.     *     * Params:     *  tmout = the maximum time to wait     * Returns:     *  `true` if the event is in signaled state, `false` if the event was nonsignaled for the given time or     *  the event is uninitialized or another error occured     */    bool wait(Duration tmout)    {        version (Windows)        {            if (!m_event)                return false;             auto maxWaitMillis = dur!("msecs")(uint.max - 1);             while (tmout > maxWaitMillis)            {                auto res = WaitForSingleObject(m_event, uint.max - 1);                if (res != WAIT_TIMEOUT)                    return res == WAIT_OBJECT_0;                tmout -= maxWaitMillis;            }            auto ms = cast(uint)(tmout.total!"msecs");            return WaitForSingleObject(m_event, ms) == WAIT_OBJECT_0;        }        else version (Posix)        {            if (!m_initalized)                return false;             pthread_mutex_lock(&m_mutex);             int result = 0;            if (!m_state)            {                if (tmout == Duration.max)                {                    result = pthread_cond_wait(&m_cond, &m_mutex);                }                else                {                    import core.sync.config;                     timespec t = void;                    mktspec(t, tmout);                     result = pthread_cond_timedwait(&m_cond, &m_mutex, &t);                }            }            if (result == 0 && !m_manualReset)                m_state = false;             pthread_mutex_unlock(&m_mutex);             return result == 0;        }    } private:    version (Windows)    {        HANDLE m_event;    }    else version (Posix)    {        pthread_mutex_t m_mutex;        pthread_cond_t m_cond;        bool m_initalized;        bool m_state;        bool m_manualReset;    }} // Test single-thread (non-shared) use.@nogc nothrow unittest{    // auto-reset, initial state false    Event ev1 = Event(false, false);    assert(!ev1.wait(1.dur!"msecs"));    ev1.setIfInitialized();    assert(ev1.wait());    assert(!ev1.wait(1.dur!"msecs"));     // manual-reset, initial state true    Event ev2 = Event(true, true);    assert(ev2.wait());    assert(ev2.wait());    ev2.reset();    assert(!ev2.wait(1.dur!"msecs"));} unittest{    import core.atomic;    import core.thread;     scope event      = new Event(true, false);    int  numThreads = 10;    shared int numRunning = 0;     void testFn()    {        event.wait(8.dur!"seconds"); // timeout below limit for druntime test_runner        numRunning.atomicOp!"+="(1);    }     auto group = new ThreadGroup;     for (int i = 0; i < numThreads; ++i)        group.create(&testFn);     auto start = MonoTime.currTime;    assert(numRunning == 0);     event.setIfInitialized();    group.joinAll();     assert(numRunning == numThreads);     assert(MonoTime.currTime - start < 5.dur!"seconds");}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t