• A Java- / .NET-like monitor

    From Bonita Montero@21:1/5 to All on Wed Nov 8 15:56:54 2023
    Java and .NET have monitor objects instead of a combination of mutexes
    and condition variables. The advantage of a monitor object is that when
    you wait for a monitor to be signalled you can wait for that and for the mutexe's semaphore to be unlocked in _one_ step. With a condition varia-
    ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.
    The below code simply has a 32 or 64 bit atomic (depening on if the 64
    bit variant is lock-free or not) and the lower half is the number of
    threads waiting to enter the mutex part and the upper half is the num-
    ber of threads waiting to be notified. As all threads wanting to be
    notified are also waiting for the mutex the lower half is always >=
    the upper half, which I check for in several asserts.
    On Windows waiting to be notified and waiting to lock the mutex part
    to be unlocked is done by WaitForMultipleObjects(). On Linux there's
    no way to wait for mutliple kernel handles to be signalled, but there
    are SystemV semaphore sets which may consist of several semaphores and
    you may have multiple operations to proceed atomically on this set.
    The drawback of combining the mutex and condition-variable parts is
    that you can't have multiple conditions associated with the same mutex.


    // monitor.h

    #pragma once
    #if defined(_WIN32)
    #define NOMINMAX
    #include <Windows.h>
    #elif defined(__unix__)
    #include <sys/types.h>
    #include <sys/sem.h>
    #include <sys/stat.h>
    #else
    #error unsupported platform
    #endif
    #include <atomic>
    #include <semaphore>
    #include <type_traits>
    #if defined(_WIN32)
    #include "xhandle.h"
    #endif

    struct monitor
    {
    monitor();
    ~monitor();
    void lock();
    void unlock();
    void wait();
    void notify();
    void notify_all();
    private:
    inline static thread_local char t_dummy;
    static constexpr bool USE64 = std::atomic_int64_t::is_always_lock_free;
    using atomic_word_t = std::conditional_t<USE64, uint64_t, uint32_t>;
    static constexpr unsigned BITS = USE64 ? 32 : 16;
    static constexpr atomic_word_t
    ENTER_VALUE = 1,
    SIGNAL_VALUE = USE64 ? 1ull << 32 : 1,
    ENTER_MASK = USE64 ? (uint32_t)-1 : (uint16_t)-1,
    SIGNAL_MASK = USE64 ? (uint64_t)(uint32_t)-1 << 32 : (uint32_t)(uint16_t)-1 << 16;
    std::atomic<atomic_word_t> m_atomic;
    std::atomic<char *> m_threadId;
    std::atomic_uint32_t m_recCount;
    #if defined(_WIN32)
    static constexpr uint32_t SEM_MAX = std::numeric_limits<LONG>::max();
    XHANDLE
    m_xhEnterEvt,
    m_xhSignalSem;
    #elif defined(__unix__)
    static constexpr uint32_t SEM_MAX = std::numeric_limits<short>::max();
    int m_sems;
    int semop( std::initializer_list<sembuf> sems );
    #endif
    };

    // monitor.cpp

    #include <iostream>
    #include <limits>
    #include <system_error>
    #include <cassert>
    #include "monitor.h"

    using namespace std;

    monitor::monitor() :
    m_atomic( 0 ),
    m_threadId( nullptr )
    #if defined(_WIN32)
    , m_xhEnterEvt( CreateEventA( nullptr, FALSE, FALSE, nullptr ) ),
    m_xhSignalSem( CreateSemaphoreA( nullptr, 0, SEM_MAX, nullptr ) )
    #elif defined(__unix__)
    , m_sems( semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR ) )
    #endif
    {
    #if defined(_WIN32)
    if( !m_xhEnterEvt.get() || !m_xhSignalSem.get() )
    throw system_error( GetLastError(), system_category(), "can't initialize monitor object" );
    #elif defined(__unix__)
    union semun { int val; void *p; } su;
    su.val = 0;
    #if defined(__linux__)
    if( m_sems == -1 )
    #else
    if( m_sems == -1 || semctl( m_sems, 0, SETVAL, su ) == -1 || semctl( m_sems, 1, SETVAL, su ) == -1 )
    #endif
    throw system_error( errno, system_category(), "can't initialize monitor object" );
    #endif
    }

    monitor::~monitor()
    {
    #if defined(__unix__)
    int ret = semctl( m_sems, 0, IPC_RMID );
    assert(ret != -1);
    #endif
    }

    void monitor::lock()
    {
    if( m_threadId.load( memory_order_relaxed ) == &t_dummy )
    {
    uint32_t oldRecCount = m_recCount.load( memory_order_relaxed );
    if( oldRecCount == (uint32_t)-1 )
    throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's recursion count saturated" );
    m_recCount.store( oldRecCount + 1, memory_order_relaxed );
    return;
    }
    atomic_word_t ref = m_atomic.load( memory_order_relaxed );
    do
    {
    if( (ref & ENTER_MASK) == ENTER_MASK )
    throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's locker count saturated" );
    assert((ref & ENTER_MASK) >= ref >> BITS);
    } while( !m_atomic.compare_exchange_strong( ref, ref + 1, memory_order_acquire, memory_order_relaxed ) );
    auto initThread = [&]()
    {
    m_threadId.store( &t_dummy, memory_order_relaxed );
    m_recCount.store( 0, memory_order_relaxed );
    };
    if( (ref & ENTER_MASK) == ref >> BITS ) [[likely]]
    return initThread();
    #if defined(_WIN32)
    if( WaitForSingleObject( m_xhEnterEvt.get(), INFINITE ) != WAIT_OBJECT_0 )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, -1, 0 } } ) )
    terminate();
    #endif
    initThread();
    }

    void monitor::unlock()
    {
    if( uint32_t rc; m_threadId.load( memory_order_relaxed ) == &t_dummy && (rc = m_recCount.load( memory_order_relaxed )) )
    {
    m_recCount.store( rc - 1, memory_order_relaxed );
    return;
    }
    atomic_word_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) && m_threadId == &t_dummy);
    m_threadId.store( nullptr, memory_order_relaxed );
    do
    assert((ref & ENTER_MASK) >= ref >> BITS);
    while( !m_atomic.compare_exchange_strong( ref, ref - 1, memory_order_release, memory_order_relaxed ) );
    if( (ref & ENTER_MASK) == 1 ) [[likely]]
    return;
    #if defined(_WIN32)
    if( !SetEvent( m_xhEnterEvt.get() ) )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, 1, IPC_NOWAIT } } ) )
    terminate();
    #endif
    }

    void monitor::wait()
    {
    assert(m_threadId == &t_dummy && !m_recCount);
    m_threadId.store( nullptr, memory_order_relaxed );
    atomic_word_t ref = m_atomic.load( memory_order_relaxed );
    do
    assert((ref & ENTER_MASK) > ref >> BITS);
    while( !m_atomic.compare_exchange_strong( ref, ref + SIGNAL_VALUE, memory_order_release, memory_order_relaxed ) );
    if( (ref & ENTER_MASK) - (ref >> BITS) > 1 )
    {
    #if defined(_WIN32)
    if( !SetEvent( m_xhEnterEvt.get() ) )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, 1, IPC_NOWAIT } } ) )
    terminate();
    #endif
    }
    #if defined(_WIN32)
    HANDLE waitFor[2] { m_xhEnterEvt.get(), m_xhSignalSem.get() };
    if( WaitForMultipleObjects( 2, waitFor, TRUE, INFINITE ) != WAIT_OBJECT_0 )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, -1, 0 }, { 1, -1, 0 } } ) )
    terminate();
    #endif
    m_threadId.store( &t_dummy, memory_order_relaxed );
    m_recCount.store( 0, memory_order_relaxed );
    }

    void monitor::notify()
    {
    atomic_word_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
    uint32_t n;
    while( (n = (uint32_t)(ref >> BITS)) && !m_atomic.compare_exchange_strong( ref, ref - SIGNAL_VALUE, memory_order_relaxed, memory_order_relaxed ) );
    if( !(ref >> BITS) )
    return;
    #if defined(_WIN32)
    if( !ReleaseSemaphore( m_xhSignalSem.get(), 1, nullptr ) )
    terminate();
    #elif defined(__unix__)
    int ret;
    do
    ret = semop( { { 1, 1, IPC_NOWAIT } } );
    while( ret == EAGAIN );
    if( ret )
    terminate();
    #endif
    }

    void monitor::notify_all()
    {
    atomic_word_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
    uint32_t n;
    while( (n = (uint32_t)(ref >> BITS)) && !m_atomic.compare_exchange_strong( ref, ref & ENTER_MASK,
    memory_order_relaxed, memory_order_relaxed ) );
    while( n )
    {
    uint32_t nRelease = n <= SEM_MAX ? n : SEM_MAX;
    #if defined(_WIN32)
    BOOL succ;
    for( ; !(succ = ReleaseSemaphore( m_xhSignalSem.get(), nRelease,
    nullptr )) && GetLastError() == ERROR_TOO_MANY_POSTS;
    nRelease = nRelease > 1 ? nRelease / 2 : nRelease );
    if( !succ )
    terminate();
    #elif defined(__unix__)
    int ret;
    for( ; (ret = semop( { { 1, (short)nRelease, IPC_NOWAIT } } )) == EAGAIN;
    nRelease = nRelease > 1 ? nRelease / 2 : nRelease );
    if( ret )
    terminate();
    #endif
    n -= nRelease;
    }
    }

    #if defined(__unix__)
    int monitor::semop( initializer_list<sembuf> sems )
    {
    int ret;
    while( (ret = ::semop( m_sems, const_cast<sembuf *>(sems.begin()), sems.size() )) == EINTR );
    return ret;
    }
    #endif

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 8 19:31:20 2023
    Am 08.11.2023 um 19:16 schrieb Kaz Kylheku:

    POSIX-style mutexes and condition variables are actually Mesa-style
    monitors.

    A monitor is different because the mutex and condition variable
    is joined in a monitor which allows the shown optimization while
    waiting to be notified.

    That's an internal detail. In the POSIX API, you have pthread_cond_wait, which looks like one operation to the caller.

    The mentioned optimization isn't possible if you don't join the
    mutex with the condition variable as I've shown; or more precisely
    there's a limit on the number of conditions as explained below.

    The problem is that you often want multiple condition variables with
    one monitor. So this is a nonstarter.

    If that would be often Java and .net would provide that.

    I suggest you make an API where the wait operation has an "int cond_index" parameter to select a condition variable.

    I've got a value which is usually 64 bit where the lower half is the
    numer if theads which want to have exclusive access to the mutex. The
    upper half is the number of threads that want to be notified. I thought
    I could split the 64 bit value in three parts, one for the first threads
    and two for the latter two types of threads. But from my eperience with
    Java and .net I thought that it doesn't happen often that you need two
    separate monitors to have twoo conditions, so I dropped this idea.

    The monitor object can be told at construction time how large a vector
    of condition variables is required.

    Then I'd had to make my atomic even more split and the number of
    threads being able to register in the bitfields of the atomic would
    be too limited. My code is to take advantage of the one-step approach
    while waiting to be notified and if you need more conditions beyond
    that you'd have to stick with the two kernel calls used with a normal
    mutex and condition variable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Wed Nov 8 18:16:36 2023
    On 2023-11-08, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Java and .NET have monitor objects instead of a combination of mutexes
    and condition variables. The advantage of a monitor object is that when

    POSIX-style mutexes and condition variables are actually Mesa-style
    monitors.

    Monitors were invented by C. A. R. Hoare ("Quicksort Guy") and another collaborator whose name escapes me, in the context of some concurrent
    Pascal experiment.

    Hoare monitors make some ordering guarantees that the "Mesa semantics"
    monitors do not. Something like that when you signal a condition
    variable, a waiting thread gets in, and when it releases the mutex, the original thread get back in (no competititon).

    The paradigm is that there is one monitor and multiple conditions.

    you wait for a monitor to be signalled you can wait for that and for the mutexe's semaphore to be unlocked in _one_ step. With a condition varia-
    ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.

    That's an internal detail. In the POSIX API, you have pthread_cond_wait,
    which looks like one operation to the caller.

    It is not required to be implemented with semaphores.

    struct monitor
    {
    monitor();
    ~monitor();
    void lock();
    void unlock();
    void wait();
    void notify();
    void notify_all();

    The problem is that you often want multiple condition variables with
    one monitor. So this is a nonstarter.

    I suggest you make an API where the wait operation has an "int cond_index" parameter to select a condition variable.

    The monitor object can be told at construction time how large a vector
    of condition variables is required.

    That still doesn't provide all the flexibility, but fits the common use
    cases where you have a monitor plus a fixed number of condition
    variables.

    It doesn't work where you have a dynamic number of condition variables;
    e.g. a dynamic set data structure has one monitor, plus a condition on
    each node it contains.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 8 18:16:47 2023
    Am 08.11.2023 um 15:56 schrieb Bonita Montero:
    Java and .NET have monitor objects instead of a combination of mutexes
    and condition variables. The advantage of a monitor object is that when
    you wait for a monitor to be signalled you can wait for that and for the mutexe's semaphore to be unlocked in _one_ step. With a condition varia-
    ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.
    The below code simply has a 32 or 64 bit atomic (depening on if the 64
    bit variant is lock-free or not) and the lower half is the number of
    threads waiting to enter the mutex part and the upper half is the num-
    ber of threads waiting to be notified. As all threads wanting to be
    notified are also waiting for the mutex the lower half is always >=
    the upper half, which I check for in several asserts.
    On Windows waiting to be notified and waiting to lock the mutex part
    to be unlocked is done by WaitForMultipleObjects(). On Linux there's
    no way to wait for mutliple kernel handles to be signalled, but there
    are SystemV semaphore sets which may consist of several semaphores and
    you may have multiple operations to proceed atomically on this set.
    The drawback of combining the mutex and condition-variable parts is
    that you can't have multiple conditions associated with the same mutex.


    // monitor.h

    #pragma once
    #if defined(_WIN32)
        #define NOMINMAX
        #include <Windows.h>
    #elif defined(__unix__)
        #include <sys/types.h>
        #include <sys/sem.h>
        #include <sys/stat.h>
    #else
        #error unsupported platform
    #endif
    #include <atomic>
    #include <semaphore>
    #include <type_traits>
    #if defined(_WIN32)
        #include "xhandle.h"
    #endif

    struct monitor
    {
        monitor();
        ~monitor();
        void lock();
        void unlock();
        void wait();
        void notify();
        void notify_all();
    private:
        inline static thread_local char t_dummy;
        static constexpr bool USE64 = std::atomic_int64_t::is_always_lock_free;
        using atomic_word_t = std::conditional_t<USE64, uint64_t, uint32_t>;
        static constexpr unsigned BITS = USE64 ? 32 : 16;
        static constexpr atomic_word_t
            ENTER_VALUE = 1,
            SIGNAL_VALUE = USE64 ? 1ull << 32 : 1,
            ENTER_MASK = USE64 ? (uint32_t)-1 : (uint16_t)-1,
            SIGNAL_MASK = USE64 ? (uint64_t)(uint32_t)-1 << 32 : (uint32_t)(uint16_t)-1 << 16;
        std::atomic<atomic_word_t> m_atomic;
        std::atomic<char *> m_threadId;
        std::atomic_uint32_t m_recCount;
    #if defined(_WIN32)
        static constexpr uint32_t SEM_MAX = std::numeric_limits<LONG>::max();
        XHANDLE
            m_xhEnterEvt,
            m_xhSignalSem;
    #elif defined(__unix__)
        static constexpr uint32_t SEM_MAX = std::numeric_limits<short>::max();
        int m_sems;
        int semop( std::initializer_list<sembuf> sems );
    #endif
    };

    // monitor.cpp

    #include <iostream>
    #include <limits>
    #include <system_error>
    #include <cassert>
    #include "monitor.h"

    using namespace std;

    monitor::monitor() :
        m_atomic( 0 ),
        m_threadId( nullptr )
    #if defined(_WIN32)
        , m_xhEnterEvt( CreateEventA( nullptr, FALSE, FALSE, nullptr ) ),
        m_xhSignalSem( CreateSemaphoreA( nullptr, 0, SEM_MAX, nullptr ) ) #elif defined(__unix__)
        , m_sems( semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR ) )
    #endif
    {
    #if defined(_WIN32)
        if( !m_xhEnterEvt.get() || !m_xhSignalSem.get() )
            throw system_error( GetLastError(), system_category(), "can't initialize monitor object" );
    #elif defined(__unix__)
        union semun { int val; void *p; } su;
        su.val = 0;
        #if defined(__linux__)
        if( m_sems == -1 )
        #else
        if( m_sems == -1 || semctl( m_sems, 0, SETVAL, su ) == -1 ||
    semctl( m_sems, 1, SETVAL, su ) == -1 )
        #endif
            throw system_error( errno, system_category(), "can't initialize
    monitor object" );
    #endif
    }

    monitor::~monitor()
    {
    #if defined(__unix__)
        int ret = semctl( m_sems, 0, IPC_RMID );
        assert(ret != -1);
    #endif
    }

    void monitor::lock()
    {
        if( m_threadId.load( memory_order_relaxed ) == &t_dummy )
        {
            uint32_t oldRecCount = m_recCount.load( memory_order_relaxed );
            if( oldRecCount == (uint32_t)-1 )
                throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's recursion count saturated" );
            m_recCount.store( oldRecCount + 1, memory_order_relaxed );
            return;
        }
        atomic_word_t ref = m_atomic.load( memory_order_relaxed );
        do
        {
            if( (ref & ENTER_MASK) == ENTER_MASK )
                throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's locker count saturated" );
            assert((ref & ENTER_MASK) >= ref >> BITS);
        } while( !m_atomic.compare_exchange_strong( ref, ref + 1, memory_order_acquire, memory_order_relaxed ) );
        auto initThread = [&]()
        {
            m_threadId.store( &t_dummy, memory_order_relaxed );
            m_recCount.store( 0, memory_order_relaxed );
        };
        if( (ref & ENTER_MASK) == ref >> BITS ) [[likely]]
            return initThread();
    #if defined(_WIN32)
        if( WaitForSingleObject( m_xhEnterEvt.get(), INFINITE ) != WAIT_OBJECT_0 )
            terminate();
    #elif defined(__unix__)
        if( semop( { { 0, -1, 0 } } ) )
            terminate();
    #endif
        initThread();
    }

    void monitor::unlock()
    {
        if( uint32_t rc; m_threadId.load( memory_order_relaxed ) ==
    &t_dummy && (rc = m_recCount.load( memory_order_relaxed )) )
        {
            m_recCount.store( rc - 1, memory_order_relaxed );
            return;
        }
        atomic_word_t ref = m_atomic.load( memory_order_relaxed );
        assert((ref & ENTER_MASK) && m_threadId == &t_dummy);
        m_threadId.store( nullptr, memory_order_relaxed );
        do
            assert((ref & ENTER_MASK) >= ref >> BITS);
        while( !m_atomic.compare_exchange_strong( ref, ref - 1, memory_order_release, memory_order_relaxed ) );
        if( (ref & ENTER_MASK) == 1 ) [[likely]]
            return;
    #if defined(_WIN32)
        if( !SetEvent( m_xhEnterEvt.get() ) )
            terminate();
    #elif defined(__unix__)
        if( semop( { { 0, 1, IPC_NOWAIT } } ) )
            terminate();
    #endif
    }

    void monitor::wait()
    {
        assert(m_threadId == &t_dummy && !m_recCount);
        m_threadId.store( nullptr, memory_order_relaxed );
        atomic_word_t ref = m_atomic.load( memory_order_relaxed );
        do
            assert((ref & ENTER_MASK) > ref >> BITS);
        while( !m_atomic.compare_exchange_strong( ref, ref + SIGNAL_VALUE, memory_order_release, memory_order_relaxed ) );
        if( (ref & ENTER_MASK) - (ref >> BITS) > 1 )
        {
    #if defined(_WIN32)
            if( !SetEvent( m_xhEnterEvt.get() ) )
                terminate();
    #elif defined(__unix__)
            if( semop( { { 0, 1, IPC_NOWAIT } } ) )
                terminate();
    #endif
        }
    #if defined(_WIN32)
        HANDLE waitFor[2] { m_xhEnterEvt.get(), m_xhSignalSem.get() };
        if( WaitForMultipleObjects( 2, waitFor, TRUE, INFINITE ) != WAIT_OBJECT_0 )
            terminate();
    #elif defined(__unix__)
        if( semop( { { 0, -1, 0 }, { 1, -1, 0 } } ) )
            terminate();
    #endif
        m_threadId.store( &t_dummy, memory_order_relaxed );
        m_recCount.store( 0, memory_order_relaxed );
    }

    void monitor::notify()
    {
        atomic_word_t ref = m_atomic.load( memory_order_relaxed );
        assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
        uint32_t n;
        while( (n = (uint32_t)(ref >> BITS)) && !m_atomic.compare_exchange_strong( ref, ref - SIGNAL_VALUE, memory_order_relaxed, memory_order_relaxed ) );
        if( !(ref >> BITS) )
            return;
    #if defined(_WIN32)
        if( !ReleaseSemaphore( m_xhSignalSem.get(), 1, nullptr ) )
            terminate();
    #elif defined(__unix__)
        int ret;
        do
            ret = semop( { { 1, 1, IPC_NOWAIT } } );
        while( ret == EAGAIN );
    while( ret == -1 && errno == EAGAIN );
        if( ret )
            terminate();
    #endif
    }

    void monitor::notify_all()
    {
        atomic_word_t ref = m_atomic.load( memory_order_relaxed );
        assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
        uint32_t n;
        while( (n = (uint32_t)(ref >> BITS)) && !m_atomic.compare_exchange_strong( ref, ref & ENTER_MASK, memory_order_relaxed, memory_order_relaxed ) );
        while( n )
        {
            uint32_t nRelease = n <= SEM_MAX ? n : SEM_MAX;
    #if defined(_WIN32)
            BOOL succ;
            for( ; !(succ = ReleaseSemaphore( m_xhSignalSem.get(), nRelease, nullptr )) && GetLastError() == ERROR_TOO_MANY_POSTS;
                nRelease = nRelease > 1 ? nRelease / 2 : nRelease );
            if( !succ )
                terminate();
    #elif defined(__unix__)
            int ret;
            for( ; (ret = semop( { { 1, (short)nRelease, IPC_NOWAIT } } ))
    == EAGAIN;
    for( ; (ret = semop( { { 1, (short)nRelease, IPC_NOWAIT } } )) == -1 &&
    errno == EAGAIN;

                nRelease = nRelease > 1 ? nRelease / 2 : nRelease );
            if( ret )
                terminate();
    #endif
            n -= nRelease;
        }
    }

    #if defined(__unix__)
    int monitor::semop( initializer_list<sembuf> sems )
    {
        int ret;
        while( (ret = ::semop( m_sems, const_cast<sembuf *>(sems.begin()), sems.size() )) == EINTR );
        return ret;
    }
    #endif

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 8 20:56:08 2023
    Am 08.11.2023 um 20:49 schrieb Kaz Kylheku:

    No, the "monitor" idea you're proposing is different in this
    way.

    That's not true. That spurious wakesups may happen with a mutex and
    a condition variable are constituted in that both are separate enti-
    ties. Spuriuos wakesups never happen with my implementation, but
    stolen wakeups are still possible.

    Monitors as they are understood in computer science (first described by
    C. A. R. Hoare) do not combine the monitor and condition variables into
    one object; they are distinct entities: one monitor, zero to many
    conditions.

    The way monitors work does not suggest an implementation, or they can
    be based internally on a mutex and a condition variable, but if you
    have a monitor that never has spurious wakeups, it is implemented
    like mine.

    To avoid muddying the debate with nonstandard terminology, you might
    want to call your cockamamie idea "bonitor".

    I've implemented a monitor without spurious wakeups.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Wed Nov 8 19:49:43 2023
    On 2023-11-08, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 08.11.2023 um 19:16 schrieb Kaz Kylheku:

    POSIX-style mutexes and condition variables are actually Mesa-style
    monitors.

    A monitor is different because the mutex and condition variable
    is joined in a monitor which allows the shown optimization while
    waiting to be notified.

    No, the "monitor" idea you're proposing is different in this
    way.

    Monitors as they are understood in computer science (first described by
    C. A. R. Hoare) do not combine the monitor and condition variables into
    one object; they are distinct entities: one monitor, zero to many
    conditions.

    To avoid muddying the debate with nonstandard terminology, you might
    want to call your cockamamie idea "bonitor".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 13:49:39 2023
    On 11/8/2023 1:41 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:16 AM, Bonita Montero wrote:
    Am 08.11.2023 um 15:56 schrieb Bonita Montero:
    Java and .NET have monitor objects instead of a combination of mutexes
    and condition variables. The advantage of a monitor object is that when
    you wait for a monitor to be signalled you can wait for that and for the >>> mutexe's semaphore to be unlocked in _one_ step. With a condition varia- >>> ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.
    The below code simply has a 32 or 64 bit atomic (depening on if the 64
    bit variant is lock-free or not) and the lower half is the number of
    threads waiting to enter the mutex part and the upper half is the num-
    ber of threads waiting to be notified. As all threads wanting to be
    notified are also waiting for the mutex the lower half is always >=
    the upper half, which I check for in several asserts.
    On Windows waiting to be notified and waiting to lock the mutex part
    to be unlocked is done by WaitForMultipleObjects(). On Linux there's
    no way to wait for mutliple kernel handles to be signalled, but there
    are SystemV semaphore sets which may consist of several semaphores and
    you may have multiple operations to proceed atomically on this set.
    The drawback of combining the mutex and condition-variable parts is
    that you can't have multiple conditions associated with the same mutex.

    [snip code]

    Model it through Relacy Race Detector first, if you get any issues, we
    can work through them. ;^)

    https://github.com/dvyukov/relacy



    There is no shame in using a race detector. If you want me to debug your
    work, well, its not going to be for free. Believe it or not its not
    exactly trivial. You already had to make corrections to your own code.

    while( ret == -1 && errno == EAGAIN );

    Keep EINTR in mind.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 13:41:04 2023
    On 11/8/2023 9:16 AM, Bonita Montero wrote:
    Am 08.11.2023 um 15:56 schrieb Bonita Montero:
    Java and .NET have monitor objects instead of a combination of mutexes
    and condition variables. The advantage of a monitor object is that when
    you wait for a monitor to be signalled you can wait for that and for the
    mutexe's semaphore to be unlocked in _one_ step. With a condition varia-
    ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.
    The below code simply has a 32 or 64 bit atomic (depening on if the 64
    bit variant is lock-free or not) and the lower half is the number of
    threads waiting to enter the mutex part and the upper half is the num-
    ber of threads waiting to be notified. As all threads wanting to be
    notified are also waiting for the mutex the lower half is always >=
    the upper half, which I check for in several asserts.
    On Windows waiting to be notified and waiting to lock the mutex part
    to be unlocked is done by WaitForMultipleObjects(). On Linux there's
    no way to wait for mutliple kernel handles to be signalled, but there
    are SystemV semaphore sets which may consist of several semaphores and
    you may have multiple operations to proceed atomically on this set.
    The drawback of combining the mutex and condition-variable parts is
    that you can't have multiple conditions associated with the same mutex.

    [snip code]

    Model it through Relacy Race Detector first, if you get any issues, we
    can work through them. ;^)

    https://github.com/dvyukov/relacy

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Wed Nov 8 23:51:21 2023
    On 2023-11-08, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 11/8/2023 11:56 AM, Bonita Montero wrote:
    I've implemented a monitor without spurious wakeups.

    Yawn.

    Funnier:

    Amine Moulay Ramdane has written seven, likewise dead in the water.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Wed Nov 8 23:25:51 2023
    On 2023-11-08, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 08.11.2023 um 20:49 schrieb Kaz Kylheku:

    No, the "monitor" idea you're proposing is different in this
    way.

    That's not true. That spurious wakesups may happen with a mutex and
    a condition variable are constituted in that both are separate enti-
    ties.

    That doesn't follow. Hoare's original monitor implementation had
    separate condition variables, yet no spurious wakeups.

    In fact, the condition wait did not require loops! Just:

    if (!whatever_condition)
    monitor.wait(whatever_cond_var);

    The thread waiting on the condition was guaranteed to get into
    the monitor with the condition still true!

    The reason we accept spurious wakeups is that the guarantee is not
    efficient on systems with multiple processors, not because
    we don't know how to code up the guarantee.

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    I've implemented a monitor without spurious wakeups.

    It doesn't look like a monitor, if it doesn't have multiple condition variables. Maybe your approach can support that.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 14:59:14 2023
    On 11/8/2023 11:56 AM, Bonita Montero wrote:
    Am 08.11.2023 um 20:49 schrieb Kaz Kylheku:

    No, the "monitor" idea you're proposing is different in this
    way.

    That's not true. That spurious wakesups may happen with a mutex and
    a condition variable are constituted in that both are separate enti-
    ties. Spuriuos wakesups never happen with my implementation, but
    stolen wakeups are still possible.

    Monitors as they are understood in computer science (first described by
    C. A. R. Hoare) do not combine the monitor and condition variables into
    one object; they are distinct entities: one monitor, zero to many
    conditions.

    The way monitors work does not suggest an implementation, or they can
    be based internally on a mutex and a condition variable, but if you
    have a monitor that never has spurious wakeups, it is implemented
    like mine.

    To avoid muddying the debate with nonstandard terminology, you might
    want to call your cockamamie idea "bonitor".

    I've implemented a monitor without spurious wakeups.


    Yawn.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 15:32:49 2023
    On 11/8/2023 11:56 AM, Bonita Montero wrote:
    Am 08.11.2023 um 20:49 schrieb Kaz Kylheku:

    No, the "monitor" idea you're proposing is different in this
    way.

    That's not true. That spurious wakesups may happen with a mutex and
    a condition variable are constituted in that both are separate enti-
    ties. Spuriuos wakesups never happen with my implementation, but
    stolen wakeups are still possible.

    Monitors as they are understood in computer science (first described by
    C. A. R. Hoare) do not combine the monitor and condition variables into
    one object; they are distinct entities: one monitor, zero to many
    conditions.

    mutex and condition variables happen to be intimately interconnected.
    Look up wait morphing...




    The way monitors work does not suggest an implementation, or they can
    be based internally on a mutex and a condition variable, but if you
    have a monitor that never has spurious wakeups, it is implemented
    like mine.

    To avoid muddying the debate with nonstandard terminology, you might
    want to call your cockamamie idea "bonitor".

    I've implemented a monitor without spurious wakeups.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 05:33:24 2023
    Am 09.11.2023 um 00:25 schrieb Kaz Kylheku:

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    Hoare monitors suck since they are less efficient.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 20:36:29 2023
    On 11/8/2023 8:33 PM, Bonita Montero wrote:
    Am 09.11.2023 um 00:25 schrieb Kaz Kylheku:

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    Hoare monitors suck since they are less efficient.

    Humm... Are you okay Bonita? Anything wrong with you?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 05:35:12 2023
    Am 09.11.2023 um 00:32 schrieb Chris M. Thomasson:

    mutex and condition variables happen to be intimately interconnected.
    Look up wait morphing...

    With my implementation registering as a thread wanting to enter the
    mutex and waiting to be notified is one atomic step. That's only
    possible if they're one part.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 05:38:16 2023
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 20:39:49 2023
    On 11/8/2023 1:49 PM, Chris M. Thomasson wrote:
    On 11/8/2023 1:41 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:16 AM, Bonita Montero wrote:
    Am 08.11.2023 um 15:56 schrieb Bonita Montero:
    Java and .NET have monitor objects instead of a combination of mutexes >>>> and condition variables. The advantage of a monitor object is that when >>>> you wait for a monitor to be signalled you can wait for that and for
    the
    mutexe's semaphore to be unlocked in _one_ step. With a condition
    varia-
    ble you have first to wait for the notify()-semaphore to be signalled
    and for the mutexe's lock in two steps.
    The below code simply has a 32 or 64 bit atomic (depening on if the 64 >>>> bit variant is lock-free or not) and the lower half is the number of
    threads waiting to enter the mutex part and the upper half is the num- >>>> ber of threads waiting to be notified. As all threads wanting to be
    notified are also waiting for the mutex the lower half is always >=
    the upper half, which I check for in several asserts.
    On Windows waiting to be notified and waiting to lock the mutex part
    to be unlocked is done by WaitForMultipleObjects(). On Linux there's
    no way to wait for mutliple kernel handles to be signalled, but there
    are SystemV semaphore sets which may consist of several semaphores and >>>> you may have multiple operations to proceed atomically on this set.
    The drawback of combining the mutex and condition-variable parts is
    that you can't have multiple conditions associated with the same mutex.

    [snip code]

    Model it through Relacy Race Detector first, if you get any issues, we
    can work through them. ;^)

    https://github.com/dvyukov/relacy



    There is no shame in using a race detector. If you want me to debug your work, well, its not going to be for free. Believe it or not its not
    exactly trivial. You already had to make corrections to your own code.

    while( ret == -1 && errno == EAGAIN );

    Keep EINTR in mind.

    Wrt your code:

    https://youtu.be/0R6WIbx8ysE

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 20:40:59 2023
    On 11/8/2023 8:37 PM, Bonita Montero wrote:
    Am 08.11.2023 um 22:49 schrieb Chris M. Thomasson:

    Keep EINTR in mind.

    EINTR is handled if you inspect my own semop overload function.


    I actually might have some free time to blow on it later on tonight.
    Humm... You are not exactly a fun person to work for.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 20:41:39 2023
    On 11/8/2023 8:36 PM, Bonita Montero wrote:
    Am 08.11.2023 um 22:41 schrieb Chris M. Thomasson:

    Model it through Relacy Race Detector first, if you get any issues, we
    can work through them. ;^)
    https://github.com/dvyukov/relacy

    You'd suggest Relacy for a hello world.



    :^D

    Hello world! Try to get it passing a Relacy test, if you are having
    trouble, I can help you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 20:42:00 2023
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.


    Yawn.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:08:39 2023
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is an superfluous extra part that takes CPU time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Wed Nov 8 21:10:41 2023
    On 11/8/2023 3:51 PM, Kaz Kylheku wrote:
    On 2023-11-08, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 11/8/2023 11:56 AM, Bonita Montero wrote:
    I've implemented a monitor without spurious wakeups.

    Yawn.

    Funnier:

    Amine Moulay Ramdane has written seven, likewise dead in the water.


    Actually, Amine had a couple of interesting ideas from years ago.
    Therefore, I think that Amine just might be "smarter" than Bonita?

    Still, wrt Anime, not sure if the ideas are original or from reading a
    white paper.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:11:43 2023
    On 11/8/2023 9:08 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is an superfluous extra part that takes CPU time.

    Look up wait morphing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:12:34 2023
    On 11/8/2023 9:11 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:08 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is an superfluous extra part that takes CPU time.

    Look up wait morphing.

    Well, I am referring to times of contention.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:17:38 2023
    Am 09.11.2023 um 06:11 schrieb Chris M. Thomasson:
    On 11/8/2023 9:08 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is an superfluous extra part that takes CPU time.

    Look up wait morphing.

    Wait morphing isn't implemented with glibc's condition variables.
    My code doen't need that because I'm sleeping on the condvar part
    and on the mutex part in *one* step.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Thu Nov 9 05:17:44 2023
    On 2023-11-09, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 09.11.2023 um 00:25 schrieb Kaz Kylheku:

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    Hoare monitors suck since they are less efficient.

    Hoare gave us the concept of monitors and condition variables,
    which deserves respect.

    The original variant is semantically useful; the guarantees that it
    provides can make it easier to reason about correctness.

    It's something to know about as part of a well-rounded education
    in concurrent programming.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:17:48 2023
    On 11/8/2023 8:35 PM, Bonita Montero wrote:
    Am 09.11.2023 um 00:32 schrieb Chris M. Thomasson:

    mutex and condition variables happen to be intimately interconnected.
    Look up wait morphing...

    With my implementation registering as a thread wanting to enter the
    mutex and waiting to be notified is one atomic step. That's only
    possible if they're one part.


    Humm... Sounds good. However, I need to try it out. Also, if you don't
    mind I might actually model it in relacy.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Wed Nov 8 21:18:37 2023
    On 11/8/2023 9:17 PM, Kaz Kylheku wrote:
    On 2023-11-09, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 09.11.2023 um 00:25 schrieb Kaz Kylheku:

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    Hoare monitors suck since they are less efficient.

    Hoare gave us the concept of monitors and condition variables,
    which deserves respect.

    The original variant is semantically useful; the guarantees that it
    provides can make it easier to reason about correctness.

    It's something to know about as part of a well-rounded education
    in concurrent programming.


    I concur with that assessment.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:19:58 2023
    Am 09.11.2023 um 06:17 schrieb Kaz Kylheku:

    Hoare gave us the concept of monitors and condition variables,
    which deserves respect.

    Hoare monitors are less efficient since they give up ownership
    of the mutex part while notifying. That are two kernel calls
    which could be prevented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:20:14 2023
    On 11/8/2023 9:18 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:17 PM, Kaz Kylheku wrote:
    On 2023-11-09, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 09.11.2023 um 00:25 schrieb Kaz Kylheku:

    Spurious wakesup are part of the "Mesa semantics" of monitors
    and condition variables, in contrast to the "Hoare semantics".

    Hoare monitors suck since they are less efficient.

    Hoare gave us the concept of monitors and condition variables,
    which deserves respect.

    The original variant is semantically useful; the guarantees that it
    provides can make it easier to reason about correctness.

    It's something to know about as part of a well-rounded education
    in concurrent programming.


    I concur with that assessment.

    I wonder if Bontia is pushing things to a borderline. Heck, he/she is
    almost making me want to work on it!!!

    https://youtu.be/rSaC-YbSDpo

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:20:48 2023
    On 11/8/2023 9:19 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:17 schrieb Kaz Kylheku:

    Hoare gave us the concept of monitors and condition variables,
    which deserves respect.

    Hoare monitors are less efficient since they give up ownership
    of the mutex part while notifying. That are two kernel calls
    which could be prevented.

    Avoiding Kernel calls is great.com.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:22:21 2023
    Am 09.11.2023 um 06:17 schrieb Chris M. Thomasson:

    Humm... Sounds good. However, I need to try it out. Also, if you don't
    mind I might actually model it in relacy.

    I've witten my own unit test. The Win32 code worked immediately,
    but the SysV-code didn't work immediately also because I forgot
    to have IPC_NOWAIT while releasing a semaphore. Why is there a
    way to wait for the release of a mutex to be accepted by another
    thread ? Who comes up with that ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:27:59 2023
    On 11/8/2023 9:22 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:17 schrieb Chris M. Thomasson:

    Humm... Sounds good. However, I need to try it out. Also, if you don't
    mind I might actually model it in relacy.

    I've witten my own unit test. The Win32 code worked immediately,
    but the SysV-code didn't work immediately also because I forgot
    to have IPC_NOWAIT while releasing a semaphore. Why is there a
    way to wait for the release of a mutex to be accepted by another
    thread ? Who comes up with that ?

    Well, invvvho, it might be prudent of me to model it in Relacy. The act
    of me porting your work over into its logic base is going to get me
    really intimate with your code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:29:44 2023
    Am 09.11.2023 um 06:27 schrieb Chris M. Thomasson:

    Well, invvvho, it might be prudent of me to model it in Relacy.
    The act of me porting your work over into its logic base is
    going to get me really intimate with your code.

    Just reading the code is easier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 05:37:24 2023
    Am 08.11.2023 um 22:49 schrieb Chris M. Thomasson:

    Keep EINTR in mind.

    EINTR is handled if you inspect my own semop overload function.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 05:36:39 2023
    Am 08.11.2023 um 22:41 schrieb Chris M. Thomasson:

    Model it through Relacy Race Detector first, if you get any issues, we
    can work through them. ;^)
    https://github.com/dvyukov/relacy

    You'd suggest Relacy for a hello world.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:30:29 2023
    On 11/8/2023 9:27 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:22 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:17 schrieb Chris M. Thomasson:

    Humm... Sounds good. However, I need to try it out. Also, if you
    don't mind I might actually model it in relacy.

    I've witten my own unit test. The Win32 code worked immediately,
    but the SysV-code didn't work immediately also because I forgot
    to have IPC_NOWAIT while releasing a semaphore. Why is there a
    way to wait for the release of a mutex to be accepted by another
    thread ? Who comes up with that ?

    Well, invvvho, it might be prudent of me to model it in Relacy. The act
    of me porting your work over into its logic base is going to get me
    really intimate with your code.

    Can you feel me? lol. ;^)

    I have to work on some of my fractal IFS right now, but, I will try to
    port your work over to Relacy. Fwiw, here is a taste of some work I ave
    to do right now:

    https://paulbourke.net/fractals/multijulia

    I am trying to create a nice volumetric form of it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:31:06 2023
    On 11/8/2023 9:29 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:27 schrieb Chris M. Thomasson:

    Well, invvvho, it might be prudent of me to model it in Relacy.
    The act  of me porting your work over into its logic base is
    going to get me really intimate with your code.

    Just reading the code is easier.

    Yup. Porting your code to Relacy is going to force me to read every damn
    line of your code. So, touche?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:32:40 2023
    On 11/8/2023 9:30 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:27 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:22 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:17 schrieb Chris M. Thomasson:

    Humm... Sounds good. However, I need to try it out. Also, if you
    don't mind I might actually model it in relacy.

    I've witten my own unit test. The Win32 code worked immediately,
    but the SysV-code didn't work immediately also because I forgot
    to have IPC_NOWAIT while releasing a semaphore. Why is there a
    way to wait for the release of a mutex to be accepted by another
    thread ? Who comes up with that ?

    Well, invvvho, it might be prudent of me to model it in Relacy. The
    act of me porting your work over into its logic base is going to get
    me really intimate with your code.

    Can you feel me? lol. ;^)

    I have to work on some of my fractal IFS right now, but, I will try to
    port your work over to Relacy. Fwiw, here is a taste of some work I ave
    to do right now:

    https://paulbourke.net/fractals/multijulia

    I am trying to create a nice volumetric form of it.

    https://youtu.be/XpbPzrSXOgk

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:34:25 2023
    Am 09.11.2023 um 06:31 schrieb Chris M. Thomasson:

    Yup. Porting your code to Relacy is going to force me to read every damn
    line of your code. So, touche?

    Reading the code doesn't hurt since the functions are short.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:36:51 2023
    On 11/8/2023 9:34 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:31 schrieb Chris M. Thomasson:

    Yup. Porting your code to Relacy is going to force me to read every
    damn line of your code. So, touche?

    Reading the code doesn't hurt since the functions are short.


    Porting your code to Relacy makes me read every damn line. You masking
    is interesting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:39:35 2023
    Am 09.11.2023 um 06:35 schrieb Chris M. Thomasson:

    Wait morphing is a way that shows how interconnected a mutex actually is
    with a condition variable...

    As you can derive from what I said I know what wait morphing is.
    I think wait morphing could be prevented unter systems supporting
    SysV seamphores by allocating a semaphore set of two semaphores
    for each mutex and leaving the second unused until you have a
    condition variable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:35:58 2023
    On 11/8/2023 9:17 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:11 schrieb Chris M. Thomasson:
    On 11/8/2023 9:08 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is an superfluous extra part that takes CPU time.

    Look up wait morphing.

    Wait morphing isn't implemented with glibc's condition variables.
    My code doen't need that because I'm sleeping on the condvar part
    and on the mutex part in *one* step.


    Wait morphing is a way that shows how interconnected a mutex actually is
    with a condition variable...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:40:53 2023
    On 11/8/2023 9:39 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:35 schrieb Chris M. Thomasson:

    Wait morphing is a way that shows how interconnected a mutex actually
    is with a condition variable...

    As you can derive from what I said I know what wait morphing is.
    I think wait morphing could be prevented unter systems supporting
    SysV seamphores by allocating a semaphore set of two semaphores
    for each mutex and leaving the second unused until you have a
    condition variable.


    Can you move waitsets over from mutex to futex and vise versa?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:40:52 2023
    Am 09.11.2023 um 06:36 schrieb Chris M. Thomasson:

    Porting your code to Relacy makes me read every damn line.
    You masking is interesting.

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:41:37 2023
    On 11/8/2023 9:40 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:39 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:35 schrieb Chris M. Thomasson:

    Wait morphing is a way that shows how interconnected a mutex actually
    is with a condition variable...

    As you can derive from what I said I know what wait morphing is.
    I think wait morphing could be prevented unter systems supporting
    SysV seamphores by allocating a semaphore set of two semaphores
    for each mutex and leaving the second unused until you have a
    condition variable.


    Can you move waitsets over from mutex to futex and vise versa?

    This is in the kernel...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:42:10 2023
    On 11/8/2023 9:40 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:36 schrieb Chris M. Thomasson:

    Porting your code to Relacy makes me read every damn line.
    You masking is interesting.

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    Oh well, like I said, you seem to be a fun person to work with...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:42:31 2023
    Am 09.11.2023 um 06:40 schrieb Chris M. Thomasson:

    Can you move waitsets over from mutex to futex and vise versa?

    glibc doesn't do this either.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:44:07 2023
    On 11/8/2023 9:42 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:40 schrieb Chris M. Thomasson:

    Can you move waitsets over from mutex to futex and vise versa?

    glibc doesn't do this either.

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:44:56 2023
    On 11/8/2023 9:44 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:42 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:40 schrieb Chris M. Thomasson:

    Can you move waitsets over from mutex to futex and vise versa?

    glibc doesn't do this either.

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    OOPS! I thought you were talking about gcc. Sorry Bonita!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:45:40 2023
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this: https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 21:48:11 2023
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this: https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:50:58 2023
    Am 09.11.2023 um 06:39 schrieb Bonita Montero:
    Am 09.11.2023 um 06:35 schrieb Chris M. Thomasson:

    Wait morphing is a way that shows how interconnected a mutex actually
    is with a condition variable...

    As you can derive from what I said I know what wait morphing is.
    I think wait morphing could be prevented unter systems supporting
    SysV seamphores by allocating a semaphore set of two semaphores
    for each mutex and leaving the second unused until you have a
    condition variable.


    Sorry, this dosn't work beyond one condvar per mutex.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:50:22 2023
    On 11/8/2023 9:48 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    I have to go to work on my fractals right now, will get back to you. I
    will mostly have time to port your code into a Relacy unit test sometime
    later on tonight or tomorrow. This work will be for free for you. Will
    you even appreciate it in any way shape or form? Or mock me?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 06:52:37 2023
    Am 09.11.2023 um 06:48 schrieb Chris M. Thomasson:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    Wait morphing isn't necessary under Win32 since you can wait
    for the mutexe's binary semaphore and for the condvar's counting
    semaphore in one step with WaitForMultipleObjects. Unfortunately
    there's nothing under Linux like that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 21:58:14 2023
    On 11/8/2023 9:50 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:48 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    I have to go to work on my fractals right now, will get back to you. I
    will mostly have time to port your code into a Relacy unit test sometime later on tonight or tomorrow. This work will be for free for you. Will
    you even appreciate it in any way shape or form? Or mock me?

    the funny thing is that I need to model one of my new wait-free queue experiments in Relacy for use in my rendering engine.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 07:32:16 2023
    Am 09.11.2023 um 06:42 schrieb Chris M. Thomasson:

    On 11/8/2023 9:40 PM, Bonita Montero wrote:

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    Oh well, like I said, you seem to be a fun person to work with...

    If you were here we would go through the code together
    and you would immediately understand it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 22:46:58 2023
    On 11/8/2023 9:58 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:50 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:48 PM, Chris M. Thomasson wrote:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel. >>>>
    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    I have to go to work on my fractals right now, will get back to you. I
    will mostly have time to port your code into a Relacy unit test
    sometime later on tonight or tomorrow. This work will be for free for
    you. Will you even appreciate it in any way shape or form? Or mock me?

    the funny thing is that I need to model one of my new wait-free queue experiments in Relacy for use in my rendering engine.

    An example of my main experiment:

    https://youtu.be/n13GHyYEfLA

    All of my own GLSL shaders, pure C++ and openGL.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 8 22:54:11 2023
    On 11/8/2023 10:32 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:42 schrieb Chris M. Thomasson:

    On 11/8/2023 9:40 PM, Bonita Montero wrote:

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    Oh well, like I said, you seem to be a fun person to work with...

    If you were here we would go through the code together
    and you would immediately understand it.


    Since I have to model one of my experimental algorithms in Relacy
    anyway, well, I will be right up in it. Wrt my code, well, its trying to
    make some fractals go volumetric and I need to highly efficient and
    specialized LIFO/FIFO stack/queue system for it. They are running on the
    CPU, I might even be able to get it run in shaders, but for now, I need
    to work on modeling my sketch of my code in Relacy, create some test
    units, and give it a go. Fwiw, here is one of my vector fields:

    https://youtu.be/poXeq5V0dso

    This used an older queue of mine to help distribute the field processing
    across multiple processors.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 23:01:10 2023
    On 11/8/2023 10:54 PM, Chris M. Thomasson wrote:
    On 11/8/2023 10:32 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:42 schrieb Chris M. Thomasson:

    On 11/8/2023 9:40 PM, Bonita Montero wrote:

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    Oh well, like I said, you seem to be a fun person to work with...

    If you were here we would go through the code together
    and you would immediately understand it.


    Since I have to model one of my experimental algorithms in Relacy
    anyway, well, I will be right up in it. Wrt my code, well, its trying to
    make some fractals go volumetric and I need to highly efficient and specialized LIFO/FIFO stack/queue system for it. They are running on the
    CPU, I might even be able to get it run in shaders, but for now, I need
    to work on modeling my sketch of my code in Relacy, create some test
    units, and give it a go. Fwiw, here is one of my vector fields:

    https://youtu.be/poXeq5V0dso

    This used an older queue of mine to help distribute the field processing across multiple processors.

    Fwiw, this one is basically embarrassingly parallel to create each
    frame. Well, that is kind of cheating wrt embarrassingly parallel, but,
    oh well:

    https://youtu.be/DrPp6xfLe4Q

    This one is from a recursive algorithm of mine, not too efficient wrt
    the generation part that gives me my field points to work with. It takes
    a while to render an animation in 4k. The recursive nature of it can
    blow a threads stack if I get too detailed. So, I need to refine my
    current quick and dirty proof of concept code, so to speak. It is kind
    of embarrassingly parallel...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 08:01:29 2023
    Am 09.11.2023 um 06:48 schrieb Chris M. Thomasson:

    Wait morphing can be highly beneficial.


    I just checkes how many voluntary context switches I have under Linux
    when having a poing-pong between two theads serving a common mutex and individual condvars.

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    #include <atomic>
    #include <sys/resource.h>

    using namespace std;

    int main()
    {
    mutex mtx;
    struct wait_t
    {
    bool signal;
    condition_variable cond;
    } waitA, waitB;
    constexpr size_t ROUNDS = 100'000;
    atomic<uint64_t> switches( 0 );
    auto thr = [&]( wait_t &waitMe, wait_t &waitYou )
    {
    for( size_t r = ROUNDS; r--; )
    {
    unique_lock<mutex> lock( mtx );
    while( !waitMe.signal )
    waitMe.cond.wait( lock );
    waitMe.signal = false;
    waitYou.signal = true;
    waitYou.cond.notify_one();
    }
    rusage ru;
    if( getrusage( RUSAGE_THREAD, &ru ) )
    terminate();
    switches += ru.ru_nvcsw;
    };
    waitA.signal = true;
    waitA.cond.notify_one();
    jthread
    thrA( thr, ref( waitA ), ref( waitB ) ),
    thrB( thr, ref( waitB ), ref( waitA ) );
    thrA.join();
    thrB.join();
    cout << switches << endl;
    }

    The code prints about ROUNDS * context switches, that's great.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 8 23:03:15 2023
    On 11/8/2023 11:01 PM, Chris M. Thomasson wrote:
    On 11/8/2023 10:54 PM, Chris M. Thomasson wrote:
    On 11/8/2023 10:32 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:42 schrieb Chris M. Thomasson:

    On 11/8/2023 9:40 PM, Bonita Montero wrote:

    My code is understandable if you know MT-primitives
    and SysV-IPC. There's nothing "damn" with my code.

    Oh well, like I said, you seem to be a fun person to work with...

    If you were here we would go through the code together
    and you would immediately understand it.


    Since I have to model one of my experimental algorithms in Relacy
    anyway, well, I will be right up in it. Wrt my code, well, its trying
    to make some fractals go volumetric and I need to highly efficient and
    specialized LIFO/FIFO stack/queue system for it. They are running on
    the CPU, I might even be able to get it run in shaders, but for now, I
    need to work on modeling my sketch of my code in Relacy, create some
    test units, and give it a go. Fwiw, here is one of my vector fields:

    https://youtu.be/poXeq5V0dso

    This used an older queue of mine to help distribute the field
    processing across multiple processors.

    Fwiw, this one is basically embarrassingly parallel to create each
    frame. Well, that is kind of cheating wrt embarrassingly parallel, but,
    oh well:

    https://youtu.be/DrPp6xfLe4Q

    This one is from a recursive algorithm of mine, not too efficient wrt
    the generation part that gives me my field points to work with. It takes
    a while to render an animation in 4k. The recursive nature of it can
    blow a threads stack if I get too detailed. So, I need to refine my
    current quick and dirty proof of concept code, so to speak. It is kind
    of embarrassingly parallel...

    oh shit, I forgot the damn link:

    https://youtu.be/YsAkm0VlCsw
    (should be 4k, damn it!)

    Also, this one is ripe for for improvement. I just know that my new
    queue system is going to work for it wrt generating its frames.

    https://youtu.be/HwIkk9zENcg

    Will create another thread to continue this.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 10:07:54 2023
    I did a test of my monitor against two mutexes and two condition
    variables, playing pingpong with each other. On Windows my imple-
    mentation is about 12 times faster than the mentioned mutex with
    condvar on a AMD 7950X Zen4 16 core system. Under WSL2 on the
    same machine the Linux implementation is 12% faster than my code.
    On Linux bare metal with a Zen2 3990X 64 core system my code is
    about 8.5% faster.
    As I recently found in this that a wait() incurs only one context
    switch back and forth I thought my code woudln't be faster, but
    on bare metal it actually is faster. And I was really surprised
    that the MS condvar implementation is that extremely slow compared
    to my monitor.

    #include <iostream>
    #include <thread>
    #include <chrono>
    #include <vector>
    #include <mutex>
    #include <condition_variable>
    #include "monitor.h"

    using namespace std;
    using namespace chrono;

    int main( int argc, char **argv )
    {
    atomic_int32_t tSum;
    auto time = [&]( auto fn )
    {
    tSum = 0;
    auto start = high_resolution_clock::now();
    fn();
    tSum += (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count();
    };
    constexpr size_t ROUNDS = 100'000;
    struct not_t
    {
    monitor mon;
    bool notifiy;
    } notA { {}, true }, notB { {}, false };
    notA.notifiy = true;
    auto monThr = [&]( not_t &me, not_t &you )
    {
    time( [&]()
    {
    for( size_t r = ROUNDS; r; --r )
    {
    me.mon.lock();
    for( ; !me.notifiy; me.mon.wait() );
    me.notifiy = false;
    me.mon.unlock();
    you.mon.lock();
    you.notifiy = true;
    you.mon.notify();
    you.mon.unlock();
    }
    } );
    };
    vector<jthread> threads;
    threads.reserve( 0 );
    threads.emplace_back( monThr, ref( notA ), ref( notB ) ),
    threads.emplace_back( monThr, ref( notB ), ref( notA ) );
    threads.resize( 0 );
    cout << tSum / ((double)ROUNDS * 2) << endl;
    struct cv_t
    {
    mutex mtx;
    condition_variable cv;
    bool signal;
    } cvA = { {}, {}, true }, cvB = { {}, {}, false };
    auto cvThr = [&]( cv_t &cvMe, cv_t &cvYou )
    {
    time( [&]()
    {
    for( size_t r = ROUNDS; r; --r )
    {
    unique_lock<mutex> lockMe( cvMe.mtx );
    for( ; !cvMe.signal; cvMe.cv.wait( lockMe ) );
    cvMe.signal = false;
    lockMe.unlock();
    unique_lock<mutex> lockYou( cvYou.mtx );
    cvYou.signal = true;
    cvYou.cv.notify_one();
    }
    } );
    };
    threads.emplace_back( cvThr, ref( cvA ), ref( cvB ) );
    threads.emplace_back( cvThr, ref( cvB ), ref( cvA ) );
    threads.resize( 0 );
    cout << tSum / ((double)ROUNDS * 2) << endl;
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Thu Nov 9 01:11:12 2023
    On 11/9/2023 1:07 AM, Bonita Montero wrote:
    I did a test of my monitor against two mutexes and two condition
    variables, playing pingpong with each other. On Windows my imple-
    mentation is about 12 times faster than the mentioned mutex with
    condvar on a AMD 7950X Zen4 16 core system. Under WSL2 on the
    same machine the Linux implementation is 12% faster than my code.
    On Linux bare metal with a Zen2 3990X 64 core system my code is
    about 8.5% faster.
    As I recently found in this that a wait() incurs only one context
    switch back and forth I thought my code woudln't be faster, but
    on bare metal it actually is faster. And I was really surprised
    that the MS condvar implementation is that extremely slow compared
    to my monitor.
    [...]

    I am just starting to model some of my queue code. Its going to fun to
    model your monitor and see if its bites the dust.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Thu Nov 9 01:22:14 2023
    On 11/9/2023 1:17 AM, Bonita Montero wrote:
    Am 09.11.2023 um 10:11 schrieb Chris M. Thomasson:

    I am just starting to model some of my queue code. Its going to fun to
    model your monitor and see if its bites the dust.

    The advantage under Linux bare metal is only 12%, if you do additional
    things in userspace the effect should become smaller. So measuring a
    simple bool ping pong shows almost the sole performance of my code.
    But you would get a noticeable difference with Windows.




    Modeling it is not about sheer performance, it is about correctness.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 9 10:17:42 2023
    Am 09.11.2023 um 10:11 schrieb Chris M. Thomasson:

    I am just starting to model some of my queue code. Its going to fun to
    model your monitor and see if its bites the dust.

    The advantage under Linux bare metal is only 12%, if you do additional
    things in userspace the effect should become smaller. So measuring a
    simple bool ping pong shows almost the sole performance of my code.
    But you would get a noticeable difference with Windows.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Thu Nov 9 01:23:02 2023
    On 11/9/2023 1:22 AM, Chris M. Thomasson wrote:
    On 11/9/2023 1:17 AM, Bonita Montero wrote:
    Am 09.11.2023 um 10:11 schrieb Chris M. Thomasson:

    I am just starting to model some of my queue code. Its going to fun
    to model your monitor and see if its bites the dust.

    The advantage under Linux bare metal is only 12%, if you do additional
    things in userspace the effect should become smaller. So measuring a
    simple bool ping pong shows almost the sole performance of my code.
    But you would get a noticeable difference with Windows.




    Modeling it is not about sheer performance, it is about correctness.

    Make sure it is sound and correct first, then we can sit back and think
    about how to make it much faster...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 10 15:08:50 2023
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

    frame #13: 0x00000001000063d4 cond_var`std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030 size=2, __sz=0) at vector:1912:15
    frame #14: 0x0000000100005eec cond_var`main(argc=1, argv=0x000000016fdff420) at test_cond.cpp:52:10
    frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Branimir Maksimovic@21:1/5 to Bonita Montero on Fri Nov 10 13:56:27 2023
    On 2023-11-09, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    I did a test of my monitor against two mutexes and two condition
    variables, playing pingpong with each other. On Windows my imple-
    mentation is about 12 times faster than the mentioned mutex with
    condvar on a AMD 7950X Zen4 16 core system. Under WSL2 on the
    same machine the Linux implementation is 12% faster than my code.
    On Linux bare metal with a Zen2 3990X 64 core system my code is
    about 8.5% faster.
    As I recently found in this that a wait() incurs only one context
    switch back and forth I thought my code woudln't be faster, but
    on bare metal it actually is faster. And I was really surprised
    that the MS condvar implementation is that extremely slow compared
    to my monitor.

    here is what i got on macOS:
    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    * frame #0: 0x000000018c33d0bc libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x000000018c374cc0 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x000000018c280a40 libsystem_c.dylib`abort + 180
    frame #3: 0x000000018c32c070 libc++abi.dylib`abort_message + 132
    frame #4: 0x000000018c31c004 libc++abi.dylib`demangling_terminate_handler() + 52
    frame #5: 0x000000018c32b434 libc++abi.dylib`std::__terminate(void (*)()) + 16
    frame #6: 0x000000018c32b390 libc++abi.dylib`std::terminate() + 36
    frame #7: 0x000000018c2aa714 libc++.1.dylib`std::__1::thread::~thread() + 32
    frame #8: 0x0000000100007cd0 cond_var`void std::__1::__destroy_at[abi:v160006]<std::__1::thread, 0>(__loc=0x00006000036c4028) at construct_at.h:66:13
    frame #9: 0x0000000100007cac cond_var`void std::__1::destroy_at[abi:v160006]<std::__1::thread, 0>(__loc=0x00006000036c4028) at construct_at.h:101:5
    frame #10: 0x0000000100007c10 cond_var`void std::__1::allocator_traits<std::__1::allocator<std::__1::thread>>::destroy[abi:v160006]<std::__1::thread, void, void>((null)=0x000000016fdff040, __p=0x00006000036c4028) at allocator_traits.h:323:9
    frame #11: 0x000000010000a090 cond_var`std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::__base_destruct_at_end[abi:v160006](this=0x000000016fdff030 size=2, __new_last=0x00006000036c4020) at vector:836:9
    frame #12: 0x0000000100009d10 cond_var`std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::__destruct_at_end[abi:v160006](this=0x000000016fdff030 size=2, __new_last=0x00006000036c4020) at vector:724:9
    frame #13: 0x00000001000063d4 cond_var`std::__1::vector<std::__1::thread, std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030 size=2, __sz=0) at vector:1912:15
    frame #14: 0x0000000100005eec cond_var`main(argc=1, argv=0x000000016fdff420) at test_cond.cpp:52:10
    frame #15: 0x000000018bff50e0 dyld`start + 2360


    --

    7-77-777, Evil Sinner! https://www.linkedin.com/in/branimir-maksimovic-6762bbaa/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 10 15:15:36 2023
    Am 10.11.2023 um 15:08 schrieb Bonita Montero:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030
    size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.

    I think I've got it: I'm using C++20 jthreads which are joined on the destruction of the jthread object. I'm just resizing the jthread vector
    to join both threads. But your jthread-implementation seems to behave
    like a normal C++11 thread which calls abort() on destruction when a
    thread which is joinable and not joind.
    You may verify that with this code.

    #include <thread>

    using namespace std;

    int main()
    {
    (void)jthread( []() { this_thread::sleep_for( 1s ); } );
    }

    The temporary is very like to be destructed before the thread
    is terminated.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Fri Nov 10 12:44:52 2023
    On 11/10/2023 6:08 AM, Bonita Montero wrote:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030
    size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.


    You should of modeled in a race-detector first!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Branimir Maksimovic@21:1/5 to Bonita Montero on Sat Nov 11 02:59:44 2023
    On 2023-11-10, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 10.11.2023 um 15:08 schrieb Bonita Montero:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030
    size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.

    I think I've got it: I'm using C++20 jthreads which are joined on the destruction of the jthread object. I'm just resizing the jthread vector
    to join both threads. But your jthread-implementation seems to behave
    like a normal C++11 thread which calls abort() on destruction when a
    thread which is joinable and not joind.
    You may verify that with this code.

    #include <thread>

    using namespace std;

    int main()
    {
    (void)jthread( []() { this_thread::sleep_for( 1s ); } );
    }

    The temporary is very like to be destructed before the thread
    is terminated.


    yes, i don't have jthread. Will try with g++ rather clang...
    Yeeee, real g++ has jthread:

    --
    la@MacBook-Air News % g++-13 -O3 cond_var.cpp test_cond.cpp -o cond_var -std=c++20 -D__unix__
    ld: warning: ignoring duplicate libraries: '-lgcc'
    Lola@MacBook-Air News % ./cond_var
    3881.83
    2984.36
    Lola@MacBook-Air News %

    7-77-777, Evil Sinner! https://www.linkedin.com/in/branimir-maksimovic-6762bbaa/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Fri Nov 10 20:09:57 2023
    On 11/10/2023 7:53 PM, Bonita Montero wrote:
    Am 10.11.2023 um 21:44 schrieb Chris M. Thomasson:
    On 11/10/2023 6:08 AM, Bonita Montero wrote:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030 size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.


    You should of modeled in a race-detector first!

    To find bugs inside his jthread-implementation ?


    To help find a bug, yup. Think it up, draft it out, create test units,
    model them with a race detector, Get it working... Then, we can think
    about improving performance.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 11 04:53:59 2023
    Am 10.11.2023 um 21:44 schrieb Chris M. Thomasson:
    On 11/10/2023 6:08 AM, Bonita Montero wrote:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030 size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.


    You should of modeled in a race-detector first!

    To find bugs inside his jthread-implementation ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Branimir Maksimovic@21:1/5 to Bonita Montero on Sat Nov 11 04:25:21 2023
    On 2023-11-11, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 10.11.2023 um 21:44 schrieb Chris M. Thomasson:
    On 11/10/2023 6:08 AM, Bonita Montero wrote:
    Am 10.11.2023 um 14:56 schrieb Branimir Maksimovic:

         frame #13: 0x00000001000063d4
    cond_var`std::__1::vector<std::__1::thread,
    std::__1::allocator<std::__1::thread>>::resize(this=0x000000016fdff030 size=2, __sz=0) at vector:1912:15
         frame #14: 0x0000000100005eec cond_var`main(argc=1,
    argv=0x000000016fdff420) at test_cond.cpp:52:10
         frame #15: 0x000000018bff50e0 dyld`start + 2360

    It seems that resizing the thread-vector while doing an emplace_back(),
    which itself seems to be inlined, fails. I don't know why.


    You should of modeled in a race-detector first!

    To find bugs inside his jthread-implementation ?

    There is no bug. I simply used thread instead of jtrhead
    as Apple g++ implementation does not have it.
    Since thread destructor throws exception
    if thread is still running, it calls terminate
    handler.
    Real g++ implementation works:
    Lola@MacBook-Air News % ./cond_var
    3566.26
    3292.95
    Lola@MacBook-Air News %
    Same thing is happening with all other
    I showed previously.
    I placed switch -std=c++20 in Apple's g++,
    but anyway it does not have jthread.
    take a look:
    Lola@MacBook-Air News % clang++ -O3 cond_var.cpp test_cond.cpp -o cond_var -std=c++20 -D__unix__
    test_cond.cpp:48:9: error: unknown type name 'jthread'; did you mean 'thread'?
    vector<jthread> threads;
    ^~~~~~~
    thread /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/thread:225:24: note: 'thread' declared here
    class _LIBCPP_TYPE_VIS thread
    ^
    1 error generated.
    Lola@MacBook-Air News %

    --

    7-77-777, Evil Sinner! https://www.linkedin.com/in/branimir-maksimovic-6762bbaa/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 11 07:07:54 2023
    Am 11.11.2023 um 05:25 schrieb Branimir Maksimovic:

    Lola@MacBook-Air News % ./cond_var
    3566.26
    3292.95

    Same as on my 3990X Linux PC: 8% faster.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 11 11:41:29 2023
    I think I've put the finishing touches to the code now. For the mutex
    part I introduced spinning, which I adopted from glibc. Spinning usually
    makes little sense for producer-consumer relationships because the time
    it takes to put an item in the queue or take it out of it is usually
    very short, and the time it takes to produce the item before and consume
    it afterwards is usually very short is usually orders of magnitude
    higher; Therefore, a collision during locking can occur quite rarely. Nevertheless, I can also imagine cases where items are produced and
    consumed with high frequency, and spinning could make sense there.

    So, here are the two changed files:

    // monitor.h

    #pragma once
    #if defined(_WIN32)
    #define NOMINMAX
    #include <Windows.h>
    #elif defined(__unix__)
    #include <sys/types.h>
    #include <sys/sem.h>
    #include <sys/stat.h>
    #else
    #error unsupported platform
    #endif
    #include <atomic>
    #include <type_traits>
    #if defined(_WIN32)
    #include "xhandle.h"
    #endif

    struct monitor
    {
    monitor( uint16_t maxSpin = 0 );
    ~monitor();
    void lock();
    void unlock();
    bool try_lock();
    void wait();
    void notify();
    void notify_all();
    uint16_t maxSpin( int16_t maxSpin );
    private:
    inline static thread_local char t_dummy;
    static constexpr bool USE64 = std::atomic_int64_t::is_always_lock_free;
    using aword_t = std::conditional_t<USE64, uint64_t, uint32_t>;
    static constexpr unsigned BITS = sizeof(aword_t) * 8 / 2;
    static constexpr aword_t
    ENTER_VALUE = 1,
    SIGNAL_VALUE = 1ull << BITS,
    ENTER_MASK = SIGNAL_VALUE - 1,
    SIGNAL_MASK = ENTER_MASK << BITS;
    std::atomic<aword_t> m_atomic;
    std::atomic<void *> m_threadId;
    uint32_t m_recCount;
    bool spinLock( aword_t &ref, bool once );
    #if defined(_WIN32)
    static constexpr uint32_t SEM_MAX = std::numeric_limits<LONG>::max();
    XHANDLE
    m_xhEnterEvt,
    m_xhSignalSem;
    #elif defined(__unix__)
    static constexpr uint32_t SEM_MAX = std::numeric_limits<short>::max();
    int m_sems;
    int semop( std::initializer_list<sembuf> sems );
    #endif
    std::atomic_uint16_t m_maxSpin, m_spinLimit;
    };

    // monitor.cpp

    #include <iostream>
    #include <limits>
    #include <system_error>
    #include <cassert>
    #if defined(__x86_64__) || defined(__i386__)
    #include <immintrin.h>
    #endif
    #include "monitor.h"

    using namespace std;

    monitor::monitor( uint16_t maxSpin ) :
    m_atomic( 0 ),
    m_threadId( nullptr ),
    #if defined(_WIN32)
    m_xhEnterEvt( CreateEventA( nullptr, FALSE, FALSE, nullptr ) ),
    m_xhSignalSem( CreateSemaphoreA( nullptr, 0, SEM_MAX, nullptr ) ), #elif defined(__unix__)
    m_sems( semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR ) ),
    #endif
    m_maxSpin( maxSpin ),
    m_spinLimit( 0 )
    {
    #if defined(_WIN32)
    if( !m_xhEnterEvt.get() || !m_xhSignalSem.get() )
    throw system_error( GetLastError(), system_category(), "can't initialize monitor object" );
    #elif defined(__unix__)
    auto zeroSem = [&]() -> bool
    {
    #if defined(__linux__)
    return true;
    #else
    short vals[2] = { 0, 0 };
    return !semctl( m_sems, 0, SETALL, vals );
    #endif
    };
    if( m_sems == -1 || zeroSem() )
    {
    int errNo = errno;
    if( m_sems != -1 )
    this->~monitor();
    throw system_error(errNo, system_category(), "can't initialize monitor
    object" );
    }
    #endif
    }

    monitor::~monitor()
    {
    #if defined(__unix__)
    int ret = semctl( m_sems, 0, IPC_RMID );
    assert(ret != -1);
    #endif
    }

    void monitor::lock()
    {
    if( m_threadId.load( memory_order_relaxed ) == &t_dummy )
    {
    if( m_recCount == (uint32_t)-1 )
    throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's recursion count saturated" );
    ++m_recCount;
    return;
    }
    aword_t ref = m_atomic.load( memory_order_relaxed );
    if( spinLock( ref, false ) )
    return;
    do
    {
    if( (ref & ENTER_MASK) == ENTER_MASK )
    throw system_error( (int)errc::result_out_of_range, generic_category(), "montor's locker count saturated" );
    assert((ref & ENTER_MASK) >= ref >> BITS);
    } while( !m_atomic.compare_exchange_strong( ref, ref + 1, memory_order_acquire, memory_order_relaxed ) );
    if( (ref & ENTER_MASK) != ref >> BITS ) [[likely]]
    {
    #if defined(_WIN32)
    if( WaitForSingleObject( m_xhEnterEvt.get(), INFINITE ) != WAIT_OBJECT_0 )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, -1, 0 } } ) == -1 )
    terminate();
    #endif
    }
    m_threadId.store( &t_dummy, memory_order_relaxed );
    m_recCount = 0;
    }

    void monitor::unlock()
    {
    if( m_threadId.load( memory_order_relaxed ) == &t_dummy && m_recCount )
    return (void)--m_recCount;
    aword_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) && m_threadId == &t_dummy);
    m_threadId.store( nullptr, memory_order_relaxed );
    do
    assert((ref & ENTER_MASK) > ref >> BITS);
    while( !m_atomic.compare_exchange_strong( ref, ref - 1, memory_order_release, memory_order_relaxed ) );
    if( (ref & ENTER_MASK) - (ref >> BITS) == 1 ) [[likely]]
    return;
    #if defined(_WIN32)
    if( !SetEvent( m_xhEnterEvt.get() ) )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, 1, IPC_NOWAIT } } ) == -1 )
    terminate();
    #endif
    }

    bool monitor::try_lock()
    {
    aword_t ref = m_atomic.load( memory_order_relaxed );
    return spinLock( ref, true );
    }

    void monitor::wait()
    {
    assert(m_threadId == &t_dummy && !m_recCount);
    m_threadId.store( nullptr, memory_order_relaxed );
    aword_t ref = m_atomic.load( memory_order_relaxed );
    do
    assert((ref & ENTER_MASK) > ref >> BITS);
    while( !m_atomic.compare_exchange_strong( ref, ref + SIGNAL_VALUE, memory_order_release, memory_order_relaxed ) );
    if( (ref & ENTER_MASK) - (ref >> BITS) > 1 )
    {
    #if defined(_WIN32)
    if( !SetEvent( m_xhEnterEvt.get() ) )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, 1, IPC_NOWAIT } } ) == -1 )
    terminate();
    #endif
    }
    #if defined(_WIN32)
    HANDLE waitFor[2] { m_xhEnterEvt.get(), m_xhSignalSem.get() };
    if( WaitForMultipleObjects( 2, waitFor, TRUE, INFINITE ) != WAIT_OBJECT_0 )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 0, -1, 0 }, { 1, -1, 0 } } ) == -1 )
    terminate();
    #endif
    m_threadId.store( &t_dummy, memory_order_relaxed );
    m_recCount = 0;
    }

    void monitor::notify()
    {
    aword_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
    do
    if( !(ref >> BITS) )
    return;
    while( !m_atomic.compare_exchange_strong( ref, ref - SIGNAL_VALUE, memory_order_relaxed, memory_order_relaxed ) );
    #if defined(_WIN32)
    if( !ReleaseSemaphore( m_xhSignalSem.get(), 1, nullptr ) )
    terminate();
    #elif defined(__unix__)
    if( semop( { { 1, 1, IPC_NOWAIT } }) == -1 )
    terminate();
    #endif
    }

    void monitor::notify_all()
    {
    aword_t ref = m_atomic.load( memory_order_relaxed );
    assert((ref & ENTER_MASK) > ref >> BITS && m_threadId == &t_dummy);
    uint32_t n;
    do
    if( !(n = (uint32_t)(ref >> BITS)) )
    return;
    while( !m_atomic.compare_exchange_strong( ref, ref & ENTER_MASK, memory_order_relaxed, memory_order_relaxed ) );
    #if defined(_WIN32)
    if( n > SEM_MAX || !ReleaseSemaphore( m_xhSignalSem.get(), n, nullptr ) )
    terminate();
    #elif defined(__unix__)
    for( uint32_t nRelease; n; n -= nRelease )
    if( semop( { { 1, (short)(nRelease = n <= SEM_MAX ? n : SEM_MAX),
    IPC_NOWAIT } } ) == -1 )
    terminate();
    #endif
    }

    uint16_t monitor::maxSpin( int16_t maxSpin )
    {
    uint16_t curMaxSpin = m_maxSpin.load( memory_order_relaxed );
    if( maxSpin >= 0 )
    m_maxSpin.store( maxSpin, memory_order_relaxed );
    return curMaxSpin;
    }

    bool monitor::spinLock( aword_t &ref, bool once )
    {
    // spinning algorithm taken from glibc
    uint32_t maxSpin = m_maxSpin.load( memory_order_relaxed );
    once = once && !maxSpin;
    maxSpin = !once ? maxSpin : 1;
    if( !maxSpin )
    return false;
    uint32_t
    prevSpinLimit = m_spinLimit.load( memory_order_relaxed ),
    spinLimit = prevSpinLimit * 2u + 10u,
    spinCount = 0;
    spinLimit = spinLimit <= maxSpin ? spinLimit : maxSpin;
    bool locked = false;
    for( ; ; ref = m_atomic.load( memory_order_relaxed ) )
    {
    assert((ref & ENTER_MASK) >= ref >> BITS);
    if( uint32_t enterers = ref & ENTER_MASK;
    enterers == ref >> BITS && enterers != ENTER_MASK
    && m_atomic.compare_exchange_strong( ref, ref + 1, memory_order_acquire, memory_order_relaxed ) )
    {
    m_threadId.store( &t_dummy, memory_order_relaxed );
    m_recCount = 0;
    locked = true;
    break;
    }
    if( ++spinCount == spinLimit )
    break;
    #if defined(_WIN32)
    YieldProcessor();
    #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__)
    || defined(__i386__))
    _mm_pause();
    #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__arm__) || defined(__aarch64__))
    __yield();
    #else
    #error "need platform-specific pause-instruction"
    #endif
    }
    if( !once ) [[likely]]
    m_spinLimit.store( (uint16_t)(prevSpinLimit + (int32_t)(spinCount -
    prevSpinLimit) / 8), memory_order_relaxed );
    return locked;
    }

    #if defined(__unix__)
    inline int monitor::semop( initializer_list<sembuf> sems )
    {
    int ret;
    while( (ret = ::semop( m_sems, const_cast<sembuf *>(sems.begin()), sems.size() )) == -1 && errno == EINTR );
    return ret;
    }
    #endif

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 11 16:39:06 2023
    Am 11.11.2023 um 11:41 schrieb Bonita Montero:

        if( m_sems == -1 || zeroSem() )
    if( m_sems == -1 || !zeroSem() )

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sat Nov 11 11:42:24 2023
    On 11/11/2023 7:39 AM, Bonita Montero wrote:
    Am 11.11.2023 um 11:41 schrieb Bonita Montero:

         if( m_sems == -1 || zeroSem() )
           if( m_sems == -1 || !zeroSem() )



    Is that yet another bug correction? Remember my advise, get it working
    then try to make it faster.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sat Nov 11 11:41:18 2023
    On 11/11/2023 2:41 AM, Bonita Montero wrote:
    I think I've put the finishing touches to the code now. For the mutex
    part I introduced spinning, which I adopted from glibc. Spinning usually
    [...]

    Food for thought... I learned something really neat over on comp.arch
    wrt Lynn Wheeler many years ago. Basically, why spin doing nothing? Oh,
    you use a yield... Well, that is still doing nothing. Think of a spin
    along the lines of:

    we try to use accomplished work as a backoff/yield for a spin...


    <quick pseudo-code>
    ______________
    void lock()
    {
    while (! mutex_trylock())
    {
    // try to do some "other" work as a
    // yield, in a sense... Hummm.... ;^)
    if (! try_to_do_some__other__work())
    {
    // failed to do some other work, lock it...
    mutex_lock();
    break;
    }
    }

    // we are locked! =^D
    }

    void unlock()
    {
    mutex_unlock();
    }
    ______________


    Well, this can be beneficial in certain setups...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Chris M. Thomasson on Sat Nov 11 17:49:01 2023
    Chris M. Thomasson wrote:
    On 11/11/2023 7:39 AM, Bonita Montero wrote:
    Am 11.11.2023 um 11:41 schrieb Bonita Montero:

         if( m_sems == -1 || zeroSem() )
            if( m_sems == -1 || !zeroSem() )



    Is that yet another bug correction? Remember my advise, get it working
    then try to make it faster.
    Chris, I think you are preaching to the deaf. I would give up 5 times
    already. Your patience is angelic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Bonita Montero on Sat Nov 11 17:45:30 2023
    Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is exactly what Java does -- and it does it for reason.
    is an superfluous extra part that takes CPU time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 12 05:40:49 2023
    Am 11.11.2023 um 20:42 schrieb Chris M. Thomasson:

    Is that yet another bug correction? Remember my advise, get it working
    then try to make it faster.

    The code immediately crashed because of an exception;
    easiest debugging.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 12 05:43:19 2023
    Am 11.11.2023 um 23:45 schrieb Pavel:
    Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is exactly what Java does -- and it does it for reason.

    No, Java and .net keep holding the mutex while doing a notify().
    That's called a Mesa monitor.

    is an superfluous extra part that takes CPU time.



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Sun Nov 12 05:02:13 2023
    On 2023-11-12, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    No, Java and .net keep holding the mutex while doing a notify().
    That's called a Mesa monitor.

    I don't suspect that is part of Mesa semantics. (It's definitely part of
    Hoare semantics.) Do you have a reference?

    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    If you're using POSIX mutexes and conditions, you should call pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.

    (In a nutshell, if you're going to be telling some thread(s) to go ahead
    and grab a mutex, get the hell out of their way first).

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 12 11:00:33 2023
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    Since under Mesa semantics, threads re-acquire the mutex with fewer guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage. In fact, a combination of mutex
    and condition variable, like a monitor implicitly is, is intended
    for procuder-consumer patterns. And at this point it never happens
    that you want to signal something but don't modify a common state.


    If you're using POSIX mutexes and conditions, you should call pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Sun Nov 12 17:53:51 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of
    Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage. In fact, a combination of mutex
    and condition variable, like a monitor implicitly is, is intended
    for procuder-consumer patterns. And at this point it never happens
    that you want to signal something but don't modify a common state.


    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Sun Nov 12 17:18:01 2023
    On 2023-11-12, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of
    Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    That doesn't say that signaling *requires* the monitor to be held,
    though!

    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage.

    No it isn't. The signal operation can trap into a kernel, which can
    require hundreds of cycles. The mutex/monitor should ideally be only
    held only as long as necessary to protect the shared variables, and not
    be extended over unrelated, lengthy operations. That encourages
    unnecessary contention.

    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    Pardon me, what is it that you believe doesn't actually happen? People
    coding this:

    // ...

    pthread_mutex_unlock(&obj->mtx);
    pthread_cond_signal(&obj->foo_cond);

    rather than this:

    // ...

    pthread_cond_signal(&obj->foo_cond);
    pthread_mutex_unlock(&obj->mtx);

    ?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sun Nov 12 12:46:34 2023
    On 11/11/2023 8:40 PM, Bonita Montero wrote:
    Am 11.11.2023 um 20:42 schrieb Chris M. Thomasson:

    Is that yet another bug correction? Remember my advise, get it working
    then try to make it faster.

    The code immediately crashed because of an exception;
    easiest debugging.


    Yet, you missed it? Actually, I am not quite sure how to parse your
    response. I have not had the free time to port your code over to a
    Relacy test unit, yet...

    :^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sun Nov 12 12:48:56 2023
    On 11/12/2023 2:00 AM, Bonita Montero wrote:
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of
    Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    Humm... A wait morph? ;^)


    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage. In fact, a combination of mutex
    and condition variable, like a monitor implicitly is, is intended
    for procuder-consumer patterns. And at this point it never happens
    that you want to signal something but don't modify a common state.


    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Sun Nov 12 12:51:29 2023
    On 11/12/2023 9:53 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of >>> Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage. In fact, a combination of mutex
    and condition variable, like a monitor implicitly is, is intended
    for procuder-consumer patterns. And at this point it never happens
    that you want to signal something but don't modify a common state.


    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    Yup. Actually, there was an old discussion about this around 20 ish
    years ago back on comp.programming.threads. David Butenhof was involved.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Bonita Montero on Sun Nov 12 19:17:19 2023
    Bonita Montero wrote:
    Am 11.11.2023 um 23:45 schrieb Pavel:
    Bonita Montero wrote:
    Am 09.11.2023 um 05:42 schrieb Chris M. Thomasson:
    On 11/8/2023 8:38 PM, Bonita Montero wrote:
    Am 09.11.2023 um 05:36 schrieb Chris M. Thomasson:

    Humm... Are you okay Bonita? Anything wrong with you?

    Hoare monitors relase a waiting thread immediately after a notify()
    and that's less efficient.

    Yawn.

    Re-acquiring the mutex part of a monitor after notify()
    is exactly what Java does -- and it does it for reason.

    No, Java and .net keep holding the mutex while doing a notify().

    Don't change the subject. Java releases lock *on waiting thread* (which
    is the behavior of a Hoare monitor by design) while waiting and then
    reacquires it after it was notified.

    RTFM for once (although, I know you won't):

    "
    public final void wait()
    throws InterruptedException

    Causes the current thread to wait until another thread invokes the
    notify() method or the notifyAll() method for this object. In other
    words, this method behaves exactly as if it simply performs the call
    wait(0).

    The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies
    threads waiting on this object's monitor to wake up either through a
    call to the notify method or the notifyAll method. The thread then waits
    until it can re-obtain ownership of the monitor and resumes execution.
    "

    That's called a Mesa monitor.

    is an superfluous extra part that takes CPU time.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Sun Nov 12 19:29:23 2023
    Kaz Kylheku wrote:
    On 2023-11-12, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    No, Java and .net keep holding the mutex while doing a notify().
    That's called a Mesa monitor.

    I don't suspect that is part of Mesa semantics. (It's definitely part of Hoare semantics.) Do you have a reference?
    She doesn't. See my citation in response to her post for the reference
    to the contrary.


    Since under Mesa semantics, threads re-acquire the mutex with fewer guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    If you're using POSIX mutexes and conditions, you should call pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be
    locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "


    (In a nutshell, if you're going to be telling some thread(s) to go ahead
    and grab a mutex, get the hell out of their way first).


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Scott Lurndal on Sun Nov 12 19:33:10 2023
    Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 12.11.2023 um 06:02 schrieb Kaz Kylheku:

    I don't suspect that is part of Mesa semantics. (It's definitely part of >>> Hoare semantics.) Do you have a reference?

    Wikipedia (https://en.wikipedia.org/wiki/Monitor_(synchronization):
    "With nonblocking condition variables (also called "Mesa style"
    condition variables or "signal and continue" condition variables),
    signaling does not cause the signaling thread to lose occupancy
    of the monitor. Instead the signaled threads are moved to the e
    queue."

    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    This is a theoretical advantage. In fact, a combination of mutex
    and condition variable, like a monitor implicitly is, is intended
    for procuder-consumer patterns. And at this point it never happens
    that you want to signal something but don't modify a common state.


    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the
    mutex, whenever possible.

    That actually never happens because you have to lock the mutex
    anyway.

    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    Why would you have additional context switches if you signal before
    releasing the lock?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Sun Nov 12 16:44:34 2023
    On 11/12/2023 4:29 PM, Pavel wrote:
    Kaz Kylheku wrote:
    On 2023-11-12, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    No, Java and .net keep holding the mutex while doing a notify().
    That's called a Mesa monitor.

    I don't suspect that is part of Mesa semantics. (It's definitely part of
    Hoare semantics.) Do you have a reference?
    She doesn't. See my citation in response to her post for the reference
    to the contrary.


    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or  not  it  currently  owns  the  mutex that
    threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable  scheduling  behavior  is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal(). > "


    (In a nutshell, if you're going to be telling some thread(s) to go ahead
    and grab a mutex, get the hell out of their way first).



    Basically, if you signal while locked then another waiting thread is
    going to try to acquire the mutex that is already locked by the
    signalling thread, instant contention. However, wait morphing techniques
    can be used to handle this since a mutex and a condvar are very intimate
    with each other anyway. Usually, signalling outside of the mutex is ideal.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Mon Nov 13 04:50:08 2023
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Scott Lurndal wrote:
    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    Why would you have additional context switches if you signal before
    releasing the lock?

    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Mon Nov 13 04:48:27 2023
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get predictable scheduling behavior just by doing that.

    Signal the condition while still holding the mutex doesn't give you any guarantees about which thread will get the mutex next.

    Suppose:

    1. The signal operation wake up the next waiting thread.

    2. The signaler then gives up the mutex.

    3. Before that awoken next-waiting-thread gets the mutex, some
    another thread comes along and seizes the mutex.

    Signaling in the mutex can blow up the critical region from "handful of instructions" to "hundreds of instructions".

    If we compare:

    mutex_lock(&stack->lock);
    node->next = stack->top;
    stack->top = node;
    mutex_unlock(&stack->lock);

    cond_signal(&stack->item_pushed);

    All that is in the critical region are the mutex are the list
    manipulation instructions, plus some of the mutex code itself.

    If we move cond_signal before mutex_unlock, everything done by that
    function, including potentially going into the kernel to wake up a
    thread, is now in the mutex.

    That's a lot to pay for some vague, unfulfillable promise of
    "predictable scheduling behavior", on which you can base approximately
    nothing.

    Hoare semantics gives you something: that if there are waiting tasks
    queued on a condition, the monitor is transferred to the first waiting
    one. *And* (I seem to recall) when that thread leaves the monitor, the
    original signaler gets it again!

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 13 07:35:04 2023
    Am 12.11.2023 um 18:53 schrieb Scott Lurndal:

    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    The context switch occurs only if _both_ conditions are met:
    the mutex is unlocked and the condition variable is signalled.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 13 07:33:36 2023
    Am 12.11.2023 um 18:18 schrieb Kaz Kylheku:

    This is a theoretical advantage.

    No it isn't. ...

    With this code singalling from inside is about 50% faster on
    my 3990X Zen2 64 core PC under Ubuntu.

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    #include <vector>

    using namespace std;

    int main( int argc, char ** )
    {
    mutex mtx;
    condition_variable cv;
    constexpr uint64_t N_ITEMS = 100'000'000;
    atomic_uint64_t itemOutput( 0 );
    auto producer = [&]()
    {
    static atomic_uint64_t itemCounter( 0 );
    while( itemCounter.fetch_add( 1, memory_order_relaxed ) < N_ITEMS )
    {
    {
    unique_lock lock( mtx );
    itemOutput.fetch_add( 1, memory_order_relaxed );
    if( argc <= 1 )
    cv.notify_one();
    }
    if( argc > 1 )
    cv.notify_one();
    }
    };
    unsigned nProducers = jthread::hardware_concurrency() - 1;
    vector<jthread> procuders;
    procuders.reserve( nProducers );
    for( unsigned t = 0; t != nProducers; ++t )
    procuders.emplace_back( producer );
    uint64_t nextItem = 0;
    for( ; ; )
    {
    unique_lock lock( mtx );
    uint64_t lastItem;
    while( (lastItem = itemOutput.load( memory_order_relaxed )) < nextItem )
    cv.wait( lock );
    if( lastItem >= N_ITEMS )
    break;
    nextItem = lastItem;
    }
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 13 07:47:09 2023
    Sorry, the code worked, but I don't need an atomic:

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    #include <vector>

    using namespace std;

    int main( int argc, char **argv )
    {
    unsigned nProducers = jthread::hardware_concurrency() - 1;
    if( (int)nProducers <= 0 )
    return EXIT_FAILURE;
    mutex mtx;
    condition_variable cv;
    constexpr uint64_t N_ITEMS = 10'000'000;
    uint64_t itemOutput = 0;
    auto producer = [&, inside = argc <= 1]()
    {
    static atomic_uint64_t itemCounter( 0 );
    while( itemCounter.fetch_add( 1, memory_order_relaxed ) < N_ITEMS )
    {
    unique_lock lock( mtx );
    ++itemOutput;
    if( inside )
    cv.notify_one();
    lock.unlock();
    if( !inside )
    cv.notify_one();
    }
    };
    vector<jthread> procuders;
    procuders.reserve( nProducers );
    for( unsigned t = 0; t != nProducers; ++t )
    procuders.emplace_back( producer );
    uint64_t nextItem = 0;
    for( ; ; )
    {
    unique_lock lock( mtx );
    uint64_t lastItem;
    while( (lastItem = itemOutput) < nextItem )
    cv.wait( lock );
    if( lastItem >= N_ITEMS )
    break;
    nextItem = lastItem;
    }
    }

    It's early in the morning ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Mon Nov 13 15:05:10 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 12.11.2023 um 18:53 schrieb Scott Lurndal:

    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    The context switch occurs only if _both_ conditions are met:
    the mutex is unlocked and the condition variable is signalled.

    Ah, your not an expert on operating systems, it seems.

    How would the OS know anything about the state of the mutex
    associated with the condition variable?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 13 16:36:31 2023
    Am 13.11.2023 um 16:05 schrieb Scott Lurndal:

    Ah, your not an expert on operating systems, it seems.

    I've shown that my monitor has a slightly better performance than the
    Linux mutex + condvar. So if they perform similar they dont have two
    context switches for being signalled _and_ beging locked. Otherwise
    the performance would be poorer.
    And this code is not part of the kernel, but it used the kernel.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 13 17:24:57 2023
    I've added some getrusage()s to take the number of voluntary context
    switches. It's almost about the same for my monitor or a mutex and a
    condvar under Linux. So the mutex plus condvar waits to be signalled
    and released in one step, just as my monitor.

    #include <iostream>
    #include <thread>
    #include <chrono>
    #include <vector>
    #include <mutex>
    #include <condition_variable>
    #include "monitor.h"
    #if defined(__unix__)
    #include <sys/resource.h>
    #endif

    using namespace std;
    using namespace chrono;

    int main( int argc, char **argv )
    {
    atomic_int64_t tSum;
    #if defined(__unix__)
    uint64_t lastVoluntary = 0;
    auto voluntary = [&]()
    {
    rusage ru;
    getrusage( RUSAGE_SELF, &ru );
    uint64_t diff = ru.ru_nvcsw - lastVoluntary;
    lastVoluntary = ru.ru_nvcsw;
    return diff;
    };
    voluntary();
    #endif
    auto time = [&]( auto fn )
    {
    tSum = 0;
    auto start = high_resolution_clock::now();
    fn();
    tSum += (int64_t)duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count();
    };
    constexpr size_t ROUNDS = 100'000;
    struct not_t
    {
    monitor mon;
    bool notifiy;
    } notA { {}, true }, notB { {}, false };
    notA.notifiy = true;
    auto monThr = [&]( not_t &me, not_t &you )
    {
    time( [&]()
    {
    for( size_t r = ROUNDS; r; --r )
    {
    me.mon.lock();
    for( ; !me.notifiy; me.mon.wait() );
    me.notifiy = false;
    me.mon.unlock();
    you.mon.lock();
    you.notifiy = true;
    you.mon.notify();
    you.mon.unlock();
    }
    } );
    };
    vector<jthread> threads;
    threads.reserve( 0 );
    threads.emplace_back( monThr, ref( notA ), ref( notB ) ),
    threads.emplace_back( monThr, ref( notB ), ref( notA ) );
    threads.resize( 0 );
    cout << (double)tSum / (2.0 * ROUNDS) << endl;
    #if defined(__unix__)
    cout << "v: " << voluntary() << endl;
    #endif
    struct cv_t
    {
    mutex mtx;
    condition_variable cv;
    bool signal;
    } cvA = { {}, {}, true }, cvB = { {}, {}, false };
    auto cvThr = [&]( cv_t &cvMe, cv_t &cvYou )
    {
    time( [&]()
    {
    for( size_t r = ROUNDS; r; --r )
    {
    unique_lock<mutex> lockMe( cvMe.mtx );
    for( ; !cvMe.signal; cvMe.cv.wait( lockMe ) );
    cvMe.signal = false;
    lockMe.unlock();
    unique_lock<mutex> lockYou( cvYou.mtx );
    cvYou.signal = true;
    cvYou.cv.notify_one();
    }
    } );
    };
    threads.emplace_back( cvThr, ref( cvA ), ref( cvB ) );
    threads.emplace_back( cvThr, ref( cvB ), ref( cvA ) );
    threads.resize( 0 );
    cout << (double)tSum / (2.0 * ROUNDS) << endl;
    #if defined(__unix__)
    cout << "v: " << voluntary() << endl;
    #endif
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 13 12:50:22 2023
    On 11/12/2023 10:47 PM, Bonita Montero wrote:
    Sorry, the code worked, but I don't need an atomic:
    [...]
    It's early in the morning ...

    Yawn. Why not make sure it works _first_!?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Mon Nov 13 14:32:32 2023
    On 11/12/2023 8:48 PM, Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java
    implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that
    threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if
    predictable scheduling behavior is required, then that mutex shall be
    locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get predictable scheduling behavior just by doing that.

    Signal the condition while still holding the mutex doesn't give you any guarantees about which thread will get the mutex next.

    Suppose:

    1. The signal operation wake up the next waiting thread.

    2. The signaler then gives up the mutex.

    3. Before that awoken next-waiting-thread gets the mutex, some
    another thread comes along and seizes the mutex.

    Signaling in the mutex can blow up the critical region from "handful of instructions" to "hundreds of instructions".

    If we compare:

    mutex_lock(&stack->lock);
    node->next = stack->top;
    stack->top = node;
    mutex_unlock(&stack->lock);

    cond_signal(&stack->item_pushed);

    All that is in the critical region are the mutex are the list
    manipulation instructions, plus some of the mutex code itself.

    If we move cond_signal before mutex_unlock, everything done by that
    function, including potentially going into the kernel to wake up a
    thread, is now in the mutex.

    Excellent point. DING!



    That's a lot to pay for some vague, unfulfillable promise of
    "predictable scheduling behavior", on which you can base approximately nothing.

    Hoare semantics gives you something: that if there are waiting tasks
    queued on a condition, the monitor is transferred to the first waiting
    one. *And* (I seem to recall) when that thread leaves the monitor, the original signaler gets it again!


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Mon Nov 13 21:27:53 2023
    Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java
    implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that
    threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if
    predictable scheduling behavior is required, then that mutex shall be
    locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get predictable scheduling behavior just by doing that.

    This is POSIX text that IIRC was a compromise with real-time (RT) guys.
    Java has thread priorities so it is formally relevant (could be relevant
    in some implementations, too; did not really check). RT guys don't like condvars in principle as they are a "gateway drug" to all kinds of prio inversions so this was a compromise that permitted the condvar to get to
    POSIX. The meaning of the spec is to prevent priority inversion when
    running on one CPU. An example problematic scenario is as follows:

    1. Higher-prio thread A grabs mutex and starts waiting for an event
    (say, posted to a queue that's currently empty). The mutex is released
    at this time to allow posting the event (the standard behavior of pthread_cond_wait()).
    2. Now that the A is not runnable, a lower-prio thread B can also run
    and suppose it also wants event from same queue.
    3. Thread C grabs mutex, posts event to the queue, releases the mutex
    4. Before thread C manages to signal the condvar (and by that wake up
    thread A), thread B grabs the lock and steals the event. Higher-prio
    thread A stays waiting for the event -- which is what RT guys hate.

    With the behavior recommended by the standard, thread C will first
    notify condvar, by that making thread A runnable and thread A will grab
    the event.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Mon Nov 13 22:05:46 2023
    Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Scott Lurndal wrote:
    No, you don't. If you updated the predicate that the condition
    variable is monitoring while holding the mutex, you should release
    the mutex before signaling or broadcasting the condition variable
    to avoid unnecessary context switches.

    Why would you have additional context switches if you signal before
    releasing the lock?

    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance
    with some scheduling policy. They don't really need to leave the
    non-runnable state until the mutex is released.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Mon Nov 13 21:58:10 2023
    Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java
    implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that
    threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if
    predictable scheduling behavior is required, then that mutex shall be
    locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get predictable scheduling behavior just by doing that.

    Signal the condition while still holding the mutex doesn't give you any guarantees about which thread will get the mutex next.

    Suppose:

    1. The signal operation wake up the next waiting thread.

    2. The signaler then gives up the mutex.

    3. Before that awoken next-waiting-thread gets the mutex, some
    another thread comes along and seizes the mutex.
    This is a gray area, the specs says:

    When each thread unblocked as a result of a pthread_cond_broadcast() or
    pthread_cond_signal() returns from its call to
    pthread_cond_wait() or pthread_cond_timedwait(), the thread shall own
    the mutex with which it called pthread_cond_wait() or
    pthread_cond_timedwait(). The thread(s) that are un‐
    blocked shall contend for the mutex according to the scheduling
    policy (if applicable), and as if each had called pthread_mutex_lock().

    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads. In other words, it is feasible reading of the
    standard that, if the pthread_cond_signal caller owns the mutex, as recommended, the scheduler may, within the signal call, get the threads
    being unblocked "to the beginning of the line (queue) of the contenders
    for the mutex" before the signalling thread has a chance to do anything
    (in particular, release the mutex). Then another thread has no chance of seizing the mutex.


    Signaling in the mutex can blow up the critical region from "handful of instructions" to "hundreds of instructions".
    RT is all about discipline to achieve low latency of serving some
    requests. It is not about high throughput.

    In fact, implementing ANY scheduling policy, even simple FIFO (although
    even defining a FIFO policy on a multi-CPU system is not as simple),
    incurs costs as compared to "no policy". RT policies are even more
    expensive. For some systems they make sense, for some they don't. POSIX
    threads extension was worked on largely in parallel with RT extension
    and this shows in the product.


    If we compare:

    mutex_lock(&stack->lock);
    node->next = stack->top;
    stack->top = node;
    mutex_unlock(&stack->lock);

    cond_signal(&stack->item_pushed);

    All that is in the critical region are the mutex are the list
    manipulation instructions, plus some of the mutex code itself.

    If we move cond_signal before mutex_unlock, everything done by that
    function, including potentially going into the kernel to wake up a
    thread, is now in the mutex.

    That's a lot to pay for some vague, unfulfillable promise of
    "predictable scheduling behavior", on which you can base approximately nothing.

    Hoare semantics gives you something: that if there are waiting tasks
    queued on a condition, the monitor is transferred to the first waiting
    one. *And* (I seem to recall) when that thread leaves the monitor, the original signaler gets it again!


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Tue Nov 14 03:30:38 2023
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance
    with some scheduling policy.

    Have you noticed how no mutex appears in the pthread_cond_signal API?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Tue Nov 14 03:27:18 2023
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 06:20:03 2023
    Am 13.11.2023 um 21:50 schrieb Chris M. Thomasson:
    On 11/12/2023 10:47 PM, Bonita Montero wrote:
    Sorry, the code worked, but I don't need an atomic:
    [...]
    It's early in the morning ...

    Yawn. Why not make sure it works _first_!?

    The first code worked but the atomic could be a normal uint64_t
    since its access is surrounded by a mutex lock and unlock.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 13 21:21:35 2023
    On 11/13/2023 9:20 PM, Bonita Montero wrote:
    Am 13.11.2023 um 21:50 schrieb Chris M. Thomasson:
    On 11/12/2023 10:47 PM, Bonita Montero wrote:
    Sorry, the code worked, but I don't need an atomic:
    [...]
    It's early in the morning ...

    Yawn. Why not make sure it works _first_!?

    The first code worked but the atomic could be a normal uint64_t
    since its access is surrounded by a mutex lock and unlock.


    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Mon Nov 13 21:28:43 2023
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well, it
    hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Mon Nov 13 21:29:45 2023
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well, it
    hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex is
    broken by default.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 07:44:43 2023
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 13 22:51:13 2023
    On 11/13/2023 10:44 PM, Bonita Montero wrote:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.


    If you say so. I have not had enough time to study up on your code. Btw,
    are you finished with any bug fixes? Are you sure it might not have any spinning out of control aspects to it? If I do model it, we need to get
    to a concrete version of it, you say this is it, I say okay, call it BonitaMonitorVersion1. Fair enough?

    https://youtu.be/T_U3Zdv8to8

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Mon Nov 13 22:46:27 2023
    On 11/11/2023 11:41 AM, Chris M. Thomasson wrote:
    On 11/11/2023 2:41 AM, Bonita Montero wrote:
    I think I've put the finishing touches to the code now. For the mutex
    part I introduced spinning, which I adopted from glibc. Spinning usually
    [...]

    Food for thought... I learned something really neat over on comp.arch
    wrt Lynn Wheeler many years ago. Basically, why spin doing nothing? Oh,
    you use a yield... Well, that is still doing nothing. Think of a spin
    along the lines of:

    we try to use accomplished work as a backoff/yield for a spin...


    <quick pseudo-code>
    ______________
    void lock()
    {
      while (! mutex_trylock())
      {
        // try to do some "other" work as a
        // yield, in a sense... Hummm.... ;^)
        if (! try_to_do_some__other__work())
        {
            // failed to do some other work, lock it...
        mutex_lock();
            break;
        }
      }

      // we are locked! =^D
    }

    void unlock()
    {
      mutex_unlock();
    }
    ______________


    Well, this can be beneficial in certain setups...

    Train Control... Can other work be done instead of spinning on a
    spinlock? Humm...

    https://youtu.be/Gm0NWMdcbmU

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Mon Nov 13 22:35:29 2023
    On 11/13/2023 7:30 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of
    pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance
    with some scheduling policy.

    Have you noticed how no mutex appears in the pthread_cond_signal API?


    DING!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 08:00:25 2023
    Am 14.11.2023 um 07:51 schrieb Chris M. Thomasson:

    If you say so. I have not had enough time to study up on your code. Btw,
    are you finished with any bug fixes? Are you sure it might not have any spinning out of control aspects to it? If I do model it, we need to get
    to a concrete version of it, you say this is it, I say okay, call it BonitaMonitorVersion1. Fair enough?

    I've got a class thead_queue which I used for years. I changed it to
    use my monitor instead a mutex plus condition variable. And the thead
    _queue is used a lot and the code that used it works as before. That's
    so much randomized synchronization for that that I for sure don't need
    any Ralacy or whatever.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Mon Nov 13 22:55:30 2023
    On 11/13/2023 10:51 PM, Chris M. Thomasson wrote:
    On 11/13/2023 10:44 PM, Bonita Montero wrote:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.


    If you say so. I have not had enough time to study up on your code. Btw,
    are you finished with any bug fixes? Are you sure it might not have any spinning out of control aspects to it? If I do model it, we need to get
    to a concrete version of it, you say this is it, I say okay, call it BonitaMonitorVersion1. Fair enough?

    https://youtu.be/T_U3Zdv8to8

    Put your version that you know works up in github or something. Name it,
    then I can get to work on it. I don't want to hear about any bug fixes
    as I am porting it over to a Relacy test unit. Fair enough? Say THIS IS IT!

    https://youtu.be/U8SSdyflGN4

    Done. Then, we can get to work.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 08:07:20 2023
    Am 14.11.2023 um 08:00 schrieb Bonita Montero:
    Am 14.11.2023 um 07:51 schrieb Chris M. Thomasson:

    If you say so. I have not had enough time to study up on your code.
    Btw, are you finished with any bug fixes? Are you sure it might not
    have any spinning out of control aspects to it? If I do model it, we
    need to get to a concrete version of it, you say this is it, I say
    okay, call it BonitaMonitorVersion1. Fair enough?

    I've got a class thead_queue ...

    This is my thread_queue in the version where I didn't use my monitor:

    #pragma once
    #include <deque>
    #include <mutex>
    #include <condition_variable>
    #include <utility>
    #include <concepts>
    #include <list>
    #include <deque>
    #include "invoke_on_destruct.h"

    template<typename Entity, typename Allocator = std::allocator<Entity>>
    struct thread_queue
    {
    thread_queue();
    explicit thread_queue( Allocator const &alloc );
    thread_queue( thread_queue const & ) = delete;
    thread_queue( thread_queue &&other );
    thread_queue &operator =( thread_queue const & ) = delete;
    thread_queue &operator =( thread_queue &&other );
    bool empty() const;
    size_t size() const;
    void shrink_to_fit();
    void clear();
    template<typename ... Args>
    requires std::is_constructible_v<Entity, Args ...>
    void enque( Args &&... args );
    template<typename Producer>
    requires requires( Producer producer ) { { producer() } -> std::same_as<std::pair<bool, Entity>>; }
    void enqueue_multiple( Producer producer );
    template<typename Consumer>
    requires requires( Consumer consumer, Entity value ) { { consumer(
    std::move( value ) ) } -> std::same_as<bool>; }
    || requires( Consumer consumer, size_t remaining, Entity value ) { {
    consumer( remaining, std::move( value ) ) } -> std::same_as<bool>; }
    void dequeue_multiple( Consumer consumer );
    Entity dequeue();
    std::deque<Entity, Allocator> deque();
    private:
    mutable std::mutex m_mtx;
    mutable std::condition_variable m_cv;
    std::deque<Entity, Allocator> m_queue;
    };

    template<typename Entity, typename Allocator>
    thread_queue<Entity, Allocator>::thread_queue()
    {
    }

    template<typename Entity, typename Allocator>
    thread_queue<Entity, Allocator>::thread_queue( Allocator const &alloc ) :
    m_queue( alloc )
    {
    }

    template<typename Entity, typename Allocator>
    thread_queue<Entity, Allocator>::thread_queue( thread_queue &&other )
    {
    std::scoped_lock lock( m_mtx );
    m_queue = move( other.m_queue );
    }

    template<typename Entity, typename Allocator>
    thread_queue<Entity, Allocator> &thread_queue<Entity, Allocator>::thread_queue::operator =( thread_queue &&other )
    {
    using namespace std;
    lock_guard
    ourlock( m_mtx ),
    otherLock( other.m_mon );
    m_queue = move( other.m_queue );
    return *this;
    }

    template<typename Entity, typename Allocator>
    bool thread_queue<Entity, Allocator>::thread_queue::empty() const
    {
    std::lock_guard lock( m_mtx );
    return m_queue.empty();
    }

    template<typename Entity, typename Allocator>
    size_t thread_queue<Entity, Allocator>::thread_queue::size() const
    {
    std::lock_guard lock( m_mtx );
    return m_queue.size();
    }

    template<typename Entity, typename Allocator>
    void thread_queue<Entity, Allocator>::thread_queue::shrink_to_fit()
    {
    std::lock_guard lock( m_mtx );
    return m_queue.shrink_to_fit();
    }

    template<typename Entity, typename Allocator>
    void thread_queue<Entity, Allocator>::thread_queue::clear()
    {
    std::lock_guard lock( m_mtx );
    m_queue.clear();
    }

    template<typename Entity, typename Allocator>
    template<typename ... Args>
    requires std::is_constructible_v<Entity, Args ...>
    void thread_queue<Entity, Allocator>::thread_queue::enque( Args &&... args )
    {
    using namespace std;
    unique_lock lock( m_mtx );
    m_queue.emplace_front( forward<Args>( args ) ... );
    m_cv.notify_one();
    }

    template<typename Entity, typename Allocator>
    Entity thread_queue<Entity, Allocator>::thread_queue::dequeue()
    {
    using namespace std;
    unique_lock lock( m_mtx );
    while( m_queue.empty() )
    m_cv.wait( lock );
    Entity value = move( m_queue.back() );
    m_queue.pop_back();
    return value;
    }

    template<typename Entity, typename Allocator>
    template<typename Producer>
    requires requires( Producer producer ) { { producer() } -> std::same_as<std::pair<bool, Entity>>; }
    void thread_queue<Entity, Allocator>::enqueue_multiple( Producer producer )
    {
    using namespace std;
    lock_guard lock( m_mtx );
    for( pair<bool, Entity> ret; (ret = move( producer() )).first; )
    m_queue.emplace_front( move( ret.second ) ),
    m_cv.notify_one();
    }

    template<typename Entity, typename Allocator>
    template<typename Consumer>
    requires requires( Consumer consumer, Entity value ) { { consumer( std::move( value ) ) } -> std::same_as<bool>; }
    || requires( Consumer consumer, size_t remaining, Entity value ) { { consumer( remaining, std::move( value ) ) } -> std::same_as<bool>; }
    void thread_queue<Entity, Allocator>::dequeue_multiple( Consumer consumer )
    {
    using namespace std;
    unique_lock lock( m_mtx );
    for( ; ; )
    {
    while( m_queue.empty() )
    m_cv.wait( lock );
    invoke_on_destruct popBack( [&]() { m_queue.pop_back(); } );
    if constexpr( requires( Consumer consumer, Entity value ) { { consumer( std::move( value ) ) } -> std::same_as<bool>; } )
    if( !consumer( move( m_queue.back() ) ) )
    return;
    else;
    else
    if( !consumer( m_queue.size(), move( m_queue.back() ) ) )
    return;
    }
    }

    template<typename Entity, typename Allocator>
    std::deque<Entity, Allocator> thread_queue<Entity, Allocator>::deque()
    {
    using namespace std;
    unique_lock lock( m_mtx );
    return move( m_queue );
    }

    Concepts rock !

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 13 23:07:58 2023
    On 11/13/2023 11:00 PM, Bonita Montero wrote:
    Am 14.11.2023 um 07:51 schrieb Chris M. Thomasson:

    If you say so. I have not had enough time to study up on your code.
    Btw, are you finished with any bug fixes? Are you sure it might not
    have any spinning out of control aspects to it? If I do model it, we
    need to get to a concrete version of it, you say this is it, I say
    okay, call it BonitaMonitorVersion1. Fair enough?

    I've got a class thead_queue which I used for years. I changed it to
    use my monitor instead a mutex plus condition variable. And the thead
    _queue is used a lot and the code that used it works as before. That's
    so much randomized synchronization for that that I for sure don't need
    any Ralacy or whatever.


    Just get a concrete version of your Monitor, say done. Think... Think
    again... No bug fixes allowed, unless Relacy finds one. Could be a very
    subtle memory order issue that you might of missed...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 13 23:08:52 2023
    On 11/13/2023 11:07 PM, Bonita Montero wrote:
    Am 14.11.2023 um 08:00 schrieb Bonita Montero:
    Am 14.11.2023 um 07:51 schrieb Chris M. Thomasson:

    If you say so. I have not had enough time to study up on your code.
    Btw, are you finished with any bug fixes? Are you sure it might not
    have any spinning out of control aspects to it? If I do model it, we
    need to get to a concrete version of it, you say this is it, I say
    okay, call it BonitaMonitorVersion1. Fair enough?

    I've got a class thead_queue ...

    This is my thread_queue in the version where I didn't use my monitor:
    [...]
    Concepts rock !

    I had your monitor on the mind...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Mon Nov 13 23:13:07 2023
    On 11/12/2023 12:46 PM, Chris M. Thomasson wrote:
    On 11/11/2023 8:40 PM, Bonita Montero wrote:
    Am 11.11.2023 um 20:42 schrieb Chris M. Thomasson:

    Is that yet another bug correction? Remember my advise, get it
    working then try to make it faster.

    The code immediately crashed because of an exception;
    easiest debugging.


    Yet, you missed it? Actually, I am not quite sure how to parse your
    response. I have not had the free time to port your code over to a
    Relacy test unit, yet...

    :^)

    Get it to a point where you can say, no more corrections are needed, for
    SURE! Then shine it one me you crazy diamond:

    https://youtu.be/54W8kktFE_o?t=524

    ;^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 10:04:39 2023
    Am 12.11.2023 um 21:46 schrieb Chris M. Thomasson:

    Yet, you missed it? Actually, I am not quite sure how to parse your
    response. I have not had the free time to port your code over to a
    Relacy test unit, yet...

    Do you really think your Relacy would have helped with my missing "!"
    with the initializtion - at a point where I didn't any synchronization
    at all ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Tue Nov 14 01:21:33 2023
    On 11/14/2023 1:04 AM, Bonita Montero wrote:
    Am 12.11.2023 um 21:46 schrieb Chris M. Thomasson:

    Yet, you missed it? Actually, I am not quite sure how to parse your
    response. I have not had the free time to port your code over to a
    Relacy test unit, yet...

    Do you really think your Relacy would have helped with my missing "!"
    with the initializtion - at a point where I didn't any synchronization
    at all ?



    My Relacy?

    Humm... Btw, my friend created it. Fwiw, I even helped him find some interesting issues it had during its very early phase. I found a couple
    of bugs in Relacy! lol. Anyway, your missing "!"? Huh?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 11:00:05 2023
    Am 14.11.2023 um 10:21 schrieb Chris M. Thomasson:

    My Relacy?

    Because you love it so much beyond necessity.

    Humm... Btw, my friend created it. Fwiw, I even helped him find some interesting issues it had during its very early phase. I found a couple
    of bugs in Relacy! lol. Anyway, your missing "!"? Huh?

    My monitor is only 230 lines of code and easy to understand; no
    necessity for Relacy. And I'm using my readers-writer-lock with
    configurable priority for writers and readers for years without
    having applied Relacy to it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Tue Nov 14 02:08:09 2023
    On 11/14/2023 2:00 AM, Bonita Montero wrote:
    Am 14.11.2023 um 10:21 schrieb Chris M. Thomasson:

    My Relacy?

    Because you love it so much beyond necessity.

    Humm... Btw, my friend created it. Fwiw, I even helped him find some
    interesting issues it had during its very early phase. I found a
    couple of bugs in Relacy! lol. Anyway, your missing "!"? Huh?

    My monitor is only 230 lines of code and easy to understand; no
    necessity for Relacy.

    So, you are finished with any future on the fly corrections, right?



    And I'm using my readers-writer-lock with
    configurable priority for writers and readers for years without
    having applied Relacy to it.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 11:23:15 2023
    Am 14.11.2023 um 11:08 schrieb Chris M. Thomasson:

    So, you are finished with any future on the fly corrections, right?

    I'm currently not missing anything with the code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Pavel on Tue Nov 14 15:12:27 2023
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote: >>> Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why Java >>> implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or not it currently owns the mutex that >>> threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if
    predictable scheduling behavior is required, then that mutex shall be >>> locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get
    predictable scheduling behavior just by doing that.

    This is POSIX text that IIRC was a compromise with real-time (RT) guys.
    Java has thread priorities so it is formally relevant (could be relevant
    in some implementations, too; did not really check). RT guys don't like >condvars in principle as they are a "gateway drug" to all kinds of prio >inversions so this was a compromise that permitted the condvar to get to >POSIX. The meaning of the spec is to prevent priority inversion when
    running on one CPU. An example problematic scenario is as follows:

    1. Higher-prio thread A grabs mutex and starts waiting for an event
    (say, posted to a queue that's currently empty). The mutex is released
    at this time to allow posting the event (the standard behavior of >pthread_cond_wait()).
    2. Now that the A is not runnable, a lower-prio thread B can also run
    and suppose it also wants event from same queue.
    3. Thread C grabs mutex, posts event to the queue, releases the mutex
    4. Before thread C manages to signal the condvar (and by that wake up
    thread A), thread B grabs the lock and steals the event. Higher-prio
    thread A stays waiting for the event -- which is what RT guys hate.

    With the behavior recommended by the standard, thread C will first
    notify condvar, by that making thread A runnable and thread A will grab
    the event.

    Clearly the application should have used a queue per priority in
    this case.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Tue Nov 14 15:18:14 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 16:46:28 2023
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform
    there's not always an easy way to have atomic 64 bit stores.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Tue Nov 14 16:09:54 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform

    32-bit platform is somewhat uncommon (outside of specialty microcontrollers like the Cortex-M7).

    I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    With x86_64 and AArch64 you _can_ rely on it. And we do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 17:41:12 2023
    Am 14.11.2023 um 17:09 schrieb Scott Lurndal:

    I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    If I do a 64 bit atomic store with MSVC or clang-cl on a 32 bit
    platform it stores the value in the stack, loads it with SSE and
    stores it atomically.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 14 18:41:41 2023
    Am 14.11.2023 um 17:41 schrieb Bonita Montero:

    If I do a 64 bit atomic store with MSVC or clang-cl on a 32 bit
    platform it stores the value in the stack, loads it with SSE and
    stores it atomically.

    I just asked myself what MSVC does if I have a 64 bit atomic store
    and I compile with -arch:IA32, i.e. without SSE. I compiled the
    following function:

    #include <atomic>

    using namespace std;

    void fn( atomic_uint64_t &a, uint64_t v )
    {
    a.store( v, memory_order_relaxed );
    }

    This is the generated code:

    ?fn@@YAXAAU?$atomic@_K@std@@_K@Z PROC
    mov eax, DWORD PTR _v$[esp-4]
    mov ecx, DWORD PTR _v$[esp]
    mov DWORD PTR tv135[esp-4], eax
    mov eax, DWORD PTR _a$[esp-4]
    mov DWORD PTR tv135[esp], ecx
    fild QWORD PTR tv135[esp-4]
    fistp QWORD PTR [eax]
    ret 0
    ?fn@@YAXAAU?$atomic@_K@std@@_K@Z ENDP

    Interestingly the resulting temporary floating point value isn't
    chopped according to the set precision in the x87 FPU control word,
    so we have all of the 64 bits inside the mantissa, of course with
    a shift.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Tue Nov 14 12:27:16 2023
    On 11/14/2023 2:23 AM, Bonita Montero wrote:
    Am 14.11.2023 um 11:08 schrieb Chris M. Thomasson:

    So, you are finished with any future on the fly corrections, right?

    I'm currently not missing anything with the code.

    Okay, can you please make a new post that contains the finished code?

    Thanks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Tue Nov 14 12:29:23 2023
    On 11/14/2023 12:27 PM, Chris M. Thomasson wrote:
    On 11/14/2023 2:23 AM, Bonita Montero wrote:
    Am 14.11.2023 um 11:08 schrieb Chris M. Thomasson:

    So, you are finished with any future on the fly corrections, right?

    I'm currently not missing anything with the code.

    Okay, can you please make a new post that contains the finished code?

    Thanks.

    Brand new thread:

    Bonita Monitor, Fin...

    Show the code, an I will work with that. Okay?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Tue Nov 14 12:25:11 2023
    On 11/14/2023 8:09 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform

    32-bit platform is somewhat uncommon (outside of specialty microcontrollers like the Cortex-M7).

    I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    Well, we have cmpxchg8b on 32-bit Intel...


    With x86_64 and AArch64 you _can_ rely on it. And we do.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Chris M. Thomasson on Tue Nov 14 22:50:43 2023
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/14/2023 8:09 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform

    32-bit platform is somewhat uncommon (outside of specialty microcontrollers
    like the Cortex-M7).

    I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    Well, we have cmpxchg8b on 32-bit Intel...

    For which a single-copy atomic semantic property is implicit
    in the operation.

    As BM pointed out, once SSE et al showed up, there was a way to
    do some 64-bit loads. Not sure they're architecturally
    required to be single-copy atomic, however.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Tue Nov 14 14:25:50 2023
    On 11/14/2023 12:25 PM, Chris M. Thomasson wrote:
    On 11/14/2023 8:09 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput
    doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform

      32-bit platform is somewhat uncommon (outside of specialty
    microcontrollers
    like the Cortex-M7).

      I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    Well, we have cmpxchg8b on 32-bit Intel...

    Also, check this out:

    https://groups.google.com/g/comp.lang.asm.x86/c/FScbTaQEYLc

    :^)




      With x86_64 and AArch64 you _can_ rely on it.   And we do.





    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Tue Nov 14 20:26:22 2023
    Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of
    pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance
    with some scheduling policy.

    Have you noticed how no mutex appears in the pthread_cond_signal API?

    Of course but so what? Nothing prevents the implementation from listing
    the temporarily released mutices of the waiting pthread_cond_wait
    callers in the cond var where pthread_cond_signal can access them.

    Or, possibly even better, keeping a pointer to them with the threads
    waiting for the condvar (there can only be at most one such temporarily released mutex per the waiting thread). And the threads waiting for the
    condvar are accessible for the pthread_cond_signal according to the spec.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Tue Nov 14 20:42:20 2023
    Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.

    My point was that the "unblocked" thread could be the thread that "won"
    the contention. "as-if" there meant that the threads didn't really need
    to be all woken up / unblocked and start contending; but the contention
    could be resolved according to the scheduling policy (the same one that
    would be applied if they were actually running and contending for the
    mutex, without any waiting for the condvar) within the signal call.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Chris M. Thomasson on Tue Nov 14 20:47:24 2023
    Chris M. Thomasson wrote:
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well, it
    hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex is
    broken by default.
    It can, it's just that the desired scheduling policy cannot be
    *effectively* applied if a program signal outside of that mutex.

    E.g. in some scenario a higher-prio thread could stay indefinitely
    starved by the lower-prio thread, regardless of the thread library implementation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Scott Lurndal on Tue Nov 14 20:35:18 2023
    Scott Lurndal wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Kaz Kylheku wrote:
    On 2023-11-13, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Kaz Kylheku wrote:
    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex, >>>>> whenever possible.
    This is recommended against by the standard for the same reason why Java >>>> implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be >>>> called by a thread whether or not it currently owns the mutex that >>>> threads calling pthread_cond_wait() or pthread_cond_timedwait() have
    associated with the condition variable during their waits; however, if >>>> predictable scheduling behavior is required, then that mutex shall be >>>> locked by the thread calling pthread_cond_broadcast() or
    pthread_cond_signal().
    "

    But that text is stupid/defective, because you will not actually get
    predictable scheduling behavior just by doing that.

    This is POSIX text that IIRC was a compromise with real-time (RT) guys.
    Java has thread priorities so it is formally relevant (could be relevant
    in some implementations, too; did not really check). RT guys don't like
    condvars in principle as they are a "gateway drug" to all kinds of prio
    inversions so this was a compromise that permitted the condvar to get to
    POSIX. The meaning of the spec is to prevent priority inversion when
    running on one CPU. An example problematic scenario is as follows:

    1. Higher-prio thread A grabs mutex and starts waiting for an event
    (say, posted to a queue that's currently empty). The mutex is released
    at this time to allow posting the event (the standard behavior of
    pthread_cond_wait()).
    2. Now that the A is not runnable, a lower-prio thread B can also run
    and suppose it also wants event from same queue.
    3. Thread C grabs mutex, posts event to the queue, releases the mutex
    4. Before thread C manages to signal the condvar (and by that wake up
    thread A), thread B grabs the lock and steals the event. Higher-prio
    thread A stays waiting for the event -- which is what RT guys hate.

    With the behavior recommended by the standard, thread C will first
    notify condvar, by that making thread A runnable and thread A will grab
    the event.

    Clearly the application should have used a queue per priority in
    this case.
    This has been an alternative for a while but traditionally RT is built
    on the threads of different prios as opposed to the resources of
    different prios.

    There are reasons for this, too: one I remember is that the ordering
    becomes better defined. (Just as an example, imagine that the events
    posted on the queue are work permits of some kind. The policy is that
    while higher-prio thread is waiting for a work permit, no lower-prio
    thread shall get a permit).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Bonita Montero on Tue Nov 14 21:10:29 2023
    Bonita Montero wrote:
    Am 09.11.2023 um 06:48 schrieb Chris M. Thomasson:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel.

    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization


    Wait morphing can be highly beneficial.

    Wait morphing isn't necessary under Win32 since you can wait
    for the mutexe's binary semaphore and for the condvar's counting
    semaphore in one step with WaitForMultipleObjects. Unfortunately
    there's nothing under Linux like that.

    Not true. A Linux program can wait for any number of eventfd semaphores
    that can be used as either binary or a counting one.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 15 05:57:01 2023
    Am 14.11.2023 um 21:25 schrieb Chris M. Thomasson:

    Well, we have cmpxchg8b on 32-bit Intel...

    That's what I also thought, i.e. you can make a 64 bit store with that.
    But that's for sure much slower than the trick with the x87-FPU I've
    shown.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 15 05:54:49 2023
    Am 14.11.2023 um 21:27 schrieb Chris M. Thomasson:
    On 11/14/2023 2:23 AM, Bonita Montero wrote:
    Am 14.11.2023 um 11:08 schrieb Chris M. Thomasson:

    So, you are finished with any future on the fly corrections, right?

    I'm currently not missing anything with the code.

    Okay, can you please make a new post that contains the finished code?

    I already posted the finished code in two parts with this little
    correction (missing "!"). The xhandle.h is still the same.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Wed Nov 15 06:07:01 2023
    Am 15.11.2023 um 03:10 schrieb Pavel:
    Bonita Montero wrote:
    Am 09.11.2023 um 06:48 schrieb Chris M. Thomasson:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the kernel. >>>>
    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization

    Wait morphing can be highly beneficial.

    Wait morphing isn't necessary under Win32 since you can wait
    for the mutexe's binary semaphore and for the condvar's counting
    semaphore in one step with WaitForMultipleObjects. Unfortunately
    there's nothing under Linux like that.

    Not true. A Linux program can wait for any number of eventfd semaphores
    that can be used as either binary or a counting one.

    An eventfd can be a semaphore, but only one semaphore and not a set
    like SysV semaphores. You can wait for multiple semaphores with poll
    or select, but a change on one semaphore releases the waiting thread
    whereas with Win32 you can wait for all conditions to be met at once.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 15 12:08:37 2023
    On 11/14/2023 8:57 PM, Bonita Montero wrote:
    Am 14.11.2023 um 21:25 schrieb Chris M. Thomasson:

    Well, we have cmpxchg8b on 32-bit Intel...

    That's what I also thought, i.e. you can make a 64 bit store with that.
    But that's for sure much slower than the trick with the x87-FPU I've
    shown.



    Are you sure its 100% atomic for use in multi-thread sync algorithms?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Wed Nov 15 12:11:33 2023
    On 11/14/2023 5:26 PM, Pavel wrote:
    Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    Kaz Kylheku wrote:
    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we
    have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of
    pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance
    with some scheduling policy.

    Have you noticed how no mutex appears in the pthread_cond_signal API?

    Of course but so what? Nothing prevents the implementation from listing
    the temporarily released mutices of the waiting pthread_cond_wait
    callers in the cond var where pthread_cond_signal can access them.

    Or, possibly even better, keeping a pointer to them with the threads waiting  for the condvar (there can only be at most one such temporarily released mutex per the waiting thread). And the threads waiting for the condvar are accessible for the pthread_cond_signal according to the spec.

    Just as long as the implementation works wrt calling pthread_cond_signal/broadcast outside of the mutex. If not, it is broken.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Wed Nov 15 12:07:20 2023
    On 11/14/2023 2:50 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/14/2023 8:09 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 16:18 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 14.11.2023 um 06:21 schrieb Chris M. Thomasson:

    You mean a word wrt the arch, or uint64_t on a 32-bit system?

    itemOutput first was a atomic_uint64_t and now is a uint64_t.
    The atomicwasn't necessary because itemOutput is accessed within
    a mutex access, i.e. it is surrounded with a lock() and unlock()
    which itself have a acquire and release behaviour, so I itemOutput >>>>>> doesn't need to be atomic.

    In most commonly used architectures a 64-bit load is single-copy
    atomic regardless of any external synchronization (e.g. a mutex,
    spin-lock or semaphore).

    Maybe, but you can't rely on that. If you have a 32 bit platform

    32-bit platform is somewhat uncommon (outside of specialty microcontrollers
    like the Cortex-M7).

    I'm not aware of any 32-bit platform that supports 64-bit
    accesses (other than via two 32-bit accesses).

    Well, we have cmpxchg8b on 32-bit Intel...

    For which a single-copy atomic semantic property is implicit
    in the operation.

    As BM pointed out, once SSE et al showed up, there was a way to
    do some 64-bit loads. Not sure they're architecturally
    required to be single-copy atomic, however.


    Right! I am not sure about that either.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Wed Nov 15 12:20:42 2023
    On 11/15/2023 12:18 PM, Chris M. Thomasson wrote:
    On 11/14/2023 5:47 PM, Pavel wrote:
    Chris M. Thomasson wrote:
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel
    <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here >>>>>> are important. The standard intent may be to permit only the threads >>>>>> that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well,
    it hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex
    is broken by default.
    It can, it's just that the desired scheduling policy cannot be
    *effectively* applied if a program signal outside of that mutex.

    E.g. in some scenario a higher-prio thread could stay indefinitely
    starved by the lower-prio thread, regardless of the thread library
    implementation.

    Afaict, it is an implementation detail. Say, SCHED_FIFO, the impl shall strive to honor this. If it has to use a bakery algorithm to do it, so
    be it.

    Also, you as the programmer can choose to signal within a locked region.
    No problem.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 15 12:21:41 2023
    On 11/14/2023 8:54 PM, Bonita Montero wrote:
    Am 14.11.2023 um 21:27 schrieb Chris M. Thomasson:
    On 11/14/2023 2:23 AM, Bonita Montero wrote:
    Am 14.11.2023 um 11:08 schrieb Chris M. Thomasson:

    So, you are finished with any future on the fly corrections, right?

    I'm currently not missing anything with the code.

    Okay, can you please make a new post that contains the finished code?

    I already posted the finished code in two parts with this little
    correction (missing "!"). The xhandle.h is still the same.



    Correction? Just post the 100% finished code in a brand new thread.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Wed Nov 15 12:18:48 2023
    On 11/14/2023 5:47 PM, Pavel wrote:
    Chris M. Thomasson wrote:
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel
    <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here >>>>> are important. The standard intent may be to permit only the threads >>>>> that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well,
    it hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex is
    broken by default.
    It can, it's just that the desired scheduling policy cannot be
    *effectively* applied if a program signal outside of that mutex.

    E.g. in some scenario a higher-prio thread could stay indefinitely
    starved by the lower-prio thread, regardless of the thread library implementation.

    Afaict, it is an implementation detail. Say, SCHED_FIFO, the impl shall
    strive to honor this. If it has to use a bakery algorithm to do it, so
    be it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Wed Nov 15 21:26:26 2023
    On 2023-11-15, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Chris M. Thomasson wrote:
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here >>>>> are important. The standard intent may be to permit only the threads >>>>> that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well, it
    hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex is
    broken by default.
    It can, it's just that the desired scheduling policy cannot be
    *effectively* applied if a program signal outside of that mutex.

    E.g. in some scenario a higher-prio thread could stay indefinitely
    starved by the lower-prio thread, regardless of the thread library implementation.

    I don't see what difference it can actually make, other than in
    situations when a thread receives a priority boost while holding a
    mutex.

    Only one of these is true: (1) either the pthread_cond_signal and pthread_cond_broadcast functions transfer threads from waiting on the
    condition to waiting on the mutex, or (2) they don't: threads are woken,
    and have to execute an operation equivalent to pthread_mutex_lock.

    (Though there is no mutex argument in the API, the thread is waiting
    with respect to a particular mutex. Each thread waiting on the same
    condition could nominate a different mutex. So for each thread waiting
    on a condition, we know which mutex it released in doing so and can
    transfer it to wait on the mutex.)

    If the pthread_cond_signal function transfers a thread from the
    condition to the mutex according to (1), then everything is cool with
    regard to scheduling.

    Whether we have this:

    pthread_mutex_unlock(&m);
    pthread_cond_signal(&c);

    ...

    // re-acquire
    pthread_mutex_lock(&m);

    or this:

    pthread_cond_signal(&c);

    pthread_mutex_unlock(&m);

    ...
    // re-acquire
    pthread_mutex_lock(&m);

    the most important thing is that the waiting threads are already
    transferred to waiting on the mutex, before the signaling thread call
    tries to re-acquire the mutex.

    I.e. the woken threads always reach the mutex wait first, regardless of
    these two orders of operations on the part of the signaling thread.

    On the other hand, if the pthread_cond_signal operation just wakes up
    the threads, such that they themselves have to call the equivalent of pthread_mutex_lock to reacquire the mutex, we do not gain any special guarantees in one order or the other. Under either order of the
    statements, the signaling thread can snatch the mutex away from the
    signaled threads, when reaching the pthread_mutex_lock call,
    even if it is a low priority thread and the others are high.

    Now with real-time priorities and mutexes that prevent priority
    inversion, we could have the following situation:

    While a low-priority thread holds a mutex, it is temporarily boosted to
    the highest priority from among the set of threads waiting for that
    mutex.

    There we have a difference between signaling inside or outside, the
    reason being that when the thread leaves the mutex to signal outside, it
    loses the temporary priority boost. That could lead to an undue delay in signaling the condition.

    The statement in the POSIX standard about predictable scheduling
    behavior is like recommending sleep calls to for obtaining more
    predictable behavior in a program riddled with race conditions.

    If I'm an implementor reading that statement, I cannot infer any
    concrete requirements about what it is that I'm supposed to do in pthread_cond_signal to bring about more predictable scheduling.

    Specifications should give specific requirements, not vague opinions.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 16 04:00:30 2023
    Am 15.11.2023 um 21:21 schrieb Chris M. Thomasson:

    Correction? Just post the 100% finished code in a brand new thread.

    I've postet everything that is necessary, you only have to add a "!".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 16 03:59:47 2023
    Am 15.11.2023 um 21:08 schrieb Chris M. Thomasson:

    Are you sure its 100% atomic for use in multi-thread sync algorithms?

    If MSVC generates that code it's atomic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Wed Nov 15 19:10:47 2023
    On 11/15/2023 7:00 PM, Bonita Montero wrote:
    Am 15.11.2023 um 21:21 schrieb Chris M. Thomasson:

    Correction? Just post the 100% finished code in a brand new thread.

    I've postet everything that is necessary, you only have to add a "!".


    Wow... Anyway, where do I add this "!"?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Thu Nov 16 03:21:49 2023
    On 2023-11-16, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 11/15/2023 7:00 PM, Bonita Montero wrote:
    Am 15.11.2023 um 21:21 schrieb Chris M. Thomasson:

    Correction? Just post the 100% finished code in a brand new thread.

    I've postet everything that is necessary, you only have to add a "!".


    Wow... Anyway, where do I add this "!"?

    I would guess, in front of "necessary".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Chris M. Thomasson on Wed Nov 15 23:07:58 2023
    Chris M. Thomasson wrote:
    On 11/14/2023 5:26 PM, Pavel wrote:
    Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    Kaz Kylheku wrote:
    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and we >>>>> have to context switch to the mutex holder which has to release the
    mutex and then switch to that signaled thread again.

    Please see my response to your other post. "as-if" calling of
    pthread_mutex_lock does not prescribe actually calling it. Nothing in
    the standard prevents pthread_cond_signal from doing all the job of
    stuffing the unblocked threads to the mutex waiting list in accordance >>>> with some scheduling policy.

    Have you noticed how no mutex appears in the pthread_cond_signal API?

    Of course but so what? Nothing prevents the implementation from
    listing the temporarily released mutices of the waiting
    pthread_cond_wait callers in the cond var where pthread_cond_signal
    can access them.

    Or, possibly even better, keeping a pointer to them with the threads
    waiting  for the condvar (there can only be at most one such
    temporarily released mutex per the waiting thread). And the threads
    waiting for the condvar are accessible for the pthread_cond_signal
    according to the spec.

    Just as long as the implementation works wrt calling pthread_cond_signal/broadcast outside of the mutex. If not, it is broken.

    I am unsure where from that discussion of implementation came. The
    *spec* permits this (signalling without holding the mutex), just warns
    that a predictable scheduling behavior cannot be achieved then.

    What would you consider an example of a "non-working" implementation?

    Does my example that illustrates how a higher-prio thread may never make progress is of a broken implementation in your opinion?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 16 06:36:19 2023
    Am 16.11.2023 um 04:10 schrieb Chris M. Thomasson:

    Wow... Anyway, where do I add this "!"?

    You don't have to change it because the Windows code was correct
    anyway.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Thu Nov 16 00:25:19 2023
    Kaz Kylheku wrote:
    On 2023-11-15, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    Chris M. Thomasson wrote:
    On 11/13/2023 9:28 PM, Chris M. Thomasson wrote:
    On 11/13/2023 7:27 PM, Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> >>>>> wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here >>>>>> are important. The standard intent may be to permit only the threads >>>>>> that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.


    A signalling thread does its thing while holding the mutex... Well, it >>>> hears the following playing in the background:

    https://youtu.be/7YvAYIJSSZY

    A condvar that cannot signal/broadcast from outside of a held mutex is
    broken by default.
    It can, it's just that the desired scheduling policy cannot be
    *effectively* applied if a program signal outside of that mutex.

    E.g. in some scenario a higher-prio thread could stay indefinitely
    starved by the lower-prio thread, regardless of the thread library
    implementation.

    I don't see what difference it can actually make, other than in
    situations when a thread receives a priority boost while holding a
    mutex.

    Only one of these is true: (1) either the pthread_cond_signal and pthread_cond_broadcast functions transfer threads from waiting on the condition to waiting on the mutex, or (2) they don't: threads are woken,
    and have to execute an operation equivalent to pthread_mutex_lock.

    (Though there is no mutex argument in the API, the thread is waiting
    with respect to a particular mutex. Each thread waiting on the same
    condition could nominate a different mutex. So for each thread waiting
    on a condition, we know which mutex it released in doing so and can
    transfer it to wait on the mutex.)

    If the pthread_cond_signal function transfers a thread from the
    condition to the mutex according to (1), then everything is cool with
    regard to scheduling.

    Whether we have this:

    pthread_mutex_unlock(&m);
    pthread_cond_signal(&c);

    ...

    // re-acquire
    pthread_mutex_lock(&m);

    or this:

    pthread_cond_signal(&c);

    pthread_mutex_unlock(&m);

    ...
    // re-acquire
    pthread_mutex_lock(&m);

    the most important thing is that the waiting threads are already
    transferred to waiting on the mutex, before the signaling thread call
    tries to re-acquire the mutex.

    I.e. the woken threads always reach the mutex wait first, regardless of
    these two orders of operations on the part of the signaling thread.

    On the other hand, if the pthread_cond_signal operation just wakes up
    the threads, such that they themselves have to call the equivalent of pthread_mutex_lock to reacquire the mutex, we do not gain any special guarantees in one order or the other. Under either order of the
    statements, the signaling thread can snatch the mutex away from the
    signaled threads, when reaching the pthread_mutex_lock call,
    even if it is a low priority thread and the others are high.

    Now with real-time priorities and mutexes that prevent priority
    inversion, we could have the following situation:

    While a low-priority thread holds a mutex, it is temporarily boosted to
    the highest priority from among the set of threads waiting for that
    mutex.

    There we have a difference between signaling inside or outside, the
    reason being that when the thread leaves the mutex to signal outside, it loses the temporary priority boost. That could lead to an undue delay in signaling the condition.

    The statement in the POSIX standard about predictable scheduling
    behavior is like recommending sleep calls to for obtaining more
    predictable behavior in a program riddled with race conditions.

    If I'm an implementor reading that statement, I cannot infer any
    concrete requirements about what it is that I'm supposed to do in pthread_cond_signal to bring about more predictable scheduling.

    Specifications should give specific requirements, not vague opinions.

    POSIX *specifies* exactly what scheduling policies a compliant
    implementation shall support (SCHED_OTHER, SCHED_FIFO, SCHED_RR, and SCHED_SPORADIC) and how exactly each of these shall behave.

    In addition to specifying the behavior, the spec *explains* the purpose
    of some policies so programmers would know when they were meant to be
    used. Explanations are there to be helpful, they are not specs. In C++ Standard, they would be formatted as a non-normative Notes.

    For example, SCHED_RR, under some conditions, has an effect "to ensure..
    that one of them [the threads] does not monopolize the processor". To
    me, this is clearly an example of "predictable scheduling behavior".

    The spec for pthread_cond_signal and broadast further simply adds one
    more condition required to achieve such behavior, namely "the signalling
    thread owns the mutex that the waiter threads used to start waiting". If
    your doesn't need a predictable scheduling behavior, your app doesn't
    have to do that.

    For an implementer, this statement is actually very useful: it permits
    them to not ensure "predictable scheduling behavior" when the cond
    waiter is signaled from a thread not owning the mutex.

    Could this statement be thrown away? -- Yes. Would this make
    implementor's life easier? -- No, it would make it much harder. The
    implementor could be bugged by the user complaints like "Your
    implementation must be broken, else why my higher-prio thread is
    starving?". With the current spec, they could answer: "This is because
    the spec allows my implementation to starve your higher-prio thread to
    death -- or exhibit any other unpredictable scheduling behavior -- if
    your condvar-signalling thread does not own the mutex that the
    higher-prio thread used to start waiting for the condvar".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Pavel on Thu Nov 16 06:39:16 2023
    On 2023-11-16, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    I am unsure where from that discussion of implementation came. The
    *spec* permits this (signalling without holding the mutex), just warns
    that a predictable scheduling behavior cannot be achieved then.

    The spec is supposed to tell implementors what to make.

    Loose wording like this is dangerous. An implementor is free to
    interpret it like this: whenever a program calls pthread_cond_signal,
    and it so happens that the calling threads owns no mutex whatsoever, the implementation is free to set a flag somewhere, based on which it will willfully damage the integrity of the scheduling implementation somehow,
    so that it behaves like crap.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 16 11:11:45 2023
    Am 13.11.2023 um 05:50 schrieb Kaz Kylheku:

    Because of the situation that the thread which was signaled is
    trying to acquire the mutex, which, stupidly, the signaling thread
    is still holding. So, oops, back it goes into suspended state, and
    we have to context switch to the mutex holder which has to release
    the mutex and then switch to that signaled thread again.

    As I've shown with the comparison of my monitor against a C++ mutex
    along with a condition variable there are no additional context swit-
    ches with that. The number of voluntary context switches is about the
    same as with my monitor, which comprehensibly waits for the mutex and
    the condition variable in one step.
    As the mutex part and the condition variable part are separate there's
    no opportunity to implement that the way I did with a SysV semaphore
    set. I guess the backing of the mutex and condition variable on Linux
    is the glibc-implementation and it uses signals for that as when you
    use a pure mutex; with that you can have an aribitrary state combina-
    tion to wake up a thread even if the states are managed separately as
    with a mutex and a condition variable.

    I simulated that with the follwing code:


    #include <iostream>
    #include <thread>
    #include <vector>
    #include <pthread.h>
    #include <sys/resource.h>

    using namespace std;

    int main()
    {
    constexpr size_t ROUNDS = 1'000;
    struct state
    {
    pthread_mutex_t mtx;
    pthread_cond_t cv;
    } a, b;
    auto init = []( state &s )
    {
    if( pthread_mutex_init( &s.mtx, nullptr )
    || pthread_cond_init( &s.cv, nullptr ) )
    terminate();
    };
    init( a );
    init( b );
    auto switches = []()
    {
    rusage ru;
    if( getrusage( RUSAGE_THREAD, &ru ) )
    terminate();
    return ru.ru_nvcsw;

    };
    atomic_uint64_t sumSwitches( 0 );
    auto thr = [&]( state &my, state &yours )
    {
    uint64_t before = switches();
    for( size_t r = ROUNDS; r--; )
    if( pthread_mutex_lock( &my.mtx )
    || pthread_cond_wait( &my.cv, &my.mtx )
    || pthread_mutex_unlock( &my.mtx )
    || pthread_mutex_lock( &yours.mtx )
    || pthread_cond_signal( &yours.cv )
    || pthread_mutex_unlock( &yours.mtx ) )
    terminate();
    sumSwitches.fetch_add( switches() - before, memory_order_relaxed );
    };
    vector<jthread> threads;
    threads.emplace_back( thr, ref( a ), ref( b ) );
    threads.emplace_back( thr, ref( b ), ref( a ) );
    if( pthread_cond_signal( &a.cv ) )
    terminate();
    threads.resize( 0 );
    cout << sumSwitches << endl;
    }

    The result is about 2'000 voluntary context switches.
    So no need to signal from outside.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Thu Nov 16 15:02:36 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 15.11.2023 um 21:08 schrieb Chris M. Thomasson:

    Are you sure its 100% atomic for use in multi-thread sync algorithms?

    If MSVC generates that code it's atomic.

    No, if Intel architecture documentation specifies that it is
    single-copy atomic, then it is single-copy atomic.

    MSVC is just a poor-to-middling C compiler.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Thu Nov 16 18:12:04 2023
    Am 16.11.2023 um 16:02 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 15.11.2023 um 21:08 schrieb Chris M. Thomasson:

    Are you sure its 100% atomic for use in multi-thread sync algorithms?

    If MSVC generates that code it's atomic.

    No, if Intel architecture documentation specifies that it is
    single-copy atomic, then it is single-copy atomic.

    MSVC relies for sure on the right behaviour.

    MSVC is just a poor-to-middling C compiler.

    MSVC has the strongest C++20-conformance but the weakest optimizer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Thu Nov 16 17:45:30 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 16.11.2023 um 16:02 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 15.11.2023 um 21:08 schrieb Chris M. Thomasson:

    Are you sure its 100% atomic for use in multi-thread sync algorithms?

    If MSVC generates that code it's atomic.

    No, if Intel architecture documentation specifies that it is
    single-copy atomic, then it is single-copy atomic.

    MSVC relies for sure on the right behaviour.

    Right. Keep thinking that.


    MSVC is just a poor-to-middling C compiler.

    MSVC has the strongest C++20-conformance but the weakest optimizer.

    Faint praise, indeed. Nobody other than Herb cares about C++20 conformance.

    Most real world C++ code relies on C++11 for portability. Most
    of our systems and our customers systems don't even support C++17.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Thu Nov 16 13:06:42 2023
    On 11/15/2023 9:36 PM, Bonita Montero wrote:
    Am 16.11.2023 um 04:10 schrieb Chris M. Thomasson:

    Wow... Anyway, where do I add this "!"?

    You don't have to change it because the Windows code was correct
    anyway.


    Huh? Do I have to correct your code with that "!" or not?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Thu Nov 16 14:16:21 2023
    On 11/12/2023 4:44 PM, Chris M. Thomasson wrote:
    On 11/12/2023 4:29 PM, Pavel wrote:
    Kaz Kylheku wrote:
    On 2023-11-12, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    No, Java and .net keep holding the mutex while doing a notify().
    That's called a Mesa monitor.

    I don't suspect that is part of Mesa semantics. (It's definitely part of >>> Hoare semantics.) Do you have a reference?
    She doesn't. See my citation in response to her post for the reference
    to the contrary.


    Since under Mesa semantics, threads re-acquire the mutex with fewer
    guarantees and must re-test the desired condition, Mesa semantics
    supports the more efficient protocol of signaling outside of the
    monitor.

    If you're using POSIX mutexes and conditions, you should call
    pthread_cond_signal and pthread_cond_broadcast outside of the mutex,
    whenever possible.
    This is recommended against by the standard for the same reason why
    Java implements Hoare monitor behavior. Citation:

    "
    The pthread_cond_broadcast() or pthread_cond_signal() functions may be
    called by a thread whether or  not  it  currently  owns  the  mutex
    that threads calling pthread_cond_wait() or pthread_cond_timedwait()
    have associated with the condition variable during their waits;
    however, if predictable  scheduling  behavior  is required, then that
    mutex shall be locked by the thread calling pthread_cond_broadcast()
    or pthread_cond_signal(). > "


    (In a nutshell, if you're going to be telling some thread(s) to go ahead >>> and grab a mutex, get the hell out of their way first).



    Basically, if you signal while locked then another waiting thread is
    going to try to acquire the mutex that is already locked by the
    signalling thread, instant contention. However, wait morphing techniques
    can be used to handle this since a mutex and a condvar are very intimate
    with each other anyway. Usually, signalling outside of the mutex is ideal.

    Actually, for some damn reason this is making me think about the
    pass-the-buck algorithm, iirc, its from SUN.

    A signal to a thread means the signaled thread already owns the mutex.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Thu Nov 16 21:48:15 2023
    Kaz Kylheku wrote:
    On 2023-11-16, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    I am unsure where from that discussion of implementation came. The
    *spec* permits this (signalling without holding the mutex), just warns
    that a predictable scheduling behavior cannot be achieved then.

    The spec is supposed to tell implementors what to make.

    Loose wording like this is dangerous. An implementor is free to
    interpret it like this: whenever a program calls pthread_cond_signal,
    and it so happens that the calling threads owns no mutex whatsoever, the implementation is free to set a flag somewhere, based on which it will willfully damage the integrity of the scheduling implementation somehow,
    so that it behaves like crap.

    If your complaint is about "predictable scheduling behavior" not being
    defined in the standard, I understand and sympathize. It could be better defined. When POSIX threads were developed, this was kinda obvious
    because RT guys required that and they know what they mean by that
    (meaning -- more or less agree). This is not a good excuse for not
    spelling it in the standard of course.

    See if this old posting of David Butenhof, one of the spec authors may
    clarify the concept:

    https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 17 06:57:08 2023
    Am 16.11.2023 um 22:06 schrieb Chris M. Thomasson:

    Huh? Do I have to correct your code with that "!" or not?

    Yes, but that was in the #if defined(__unix__) branch.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Pavel on Fri Nov 17 16:29:08 2023
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Kaz Kylheku wrote:
    On 2023-11-16, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote: >>> I am unsure where from that discussion of implementation came. The
    *spec* permits this (signalling without holding the mutex), just warns
    that a predictable scheduling behavior cannot be achieved then.

    The spec is supposed to tell implementors what to make.

    Loose wording like this is dangerous. An implementor is free to
    interpret it like this: whenever a program calls pthread_cond_signal,
    and it so happens that the calling threads owns no mutex whatsoever, the
    implementation is free to set a flag somewhere, based on which it will
    willfully damage the integrity of the scheduling implementation somehow,
    so that it behaves like crap.

    If your complaint is about "predictable scheduling behavior" not being >defined in the standard, I understand and sympathize. It could be better >defined. When POSIX threads were developed, this was kinda obvious
    because RT guys required that and they know what they mean by that
    (meaning -- more or less agree). This is not a good excuse for not
    spelling it in the standard of course.

    See if this old posting of David Butenhof, one of the spec authors may >clarify the concept:

    https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    When David wrote that, multiprocessor systems were still rare and
    limited to very expensive systems (e.g. Sequent) (and for the most
    part, weren't running real-time code). I was on the X/Open working group for
    a few years during that time period, and one of my collegues at
    SGI was part of the 1003.4 working group.

    thirty years later, pthread_cond_t is heavily used by non-realtime
    code, where such priority inversions (usually very transient) don't
    matter.

    For the number of cases where pthread_cond_t is used in hard
    real-time code, the appropriate techniques (i.e. holding the mutex
    while signaling the condition) are available.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Fri Nov 17 10:22:35 2023
    On 11/17/2023 8:29 AM, Scott Lurndal wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Kaz Kylheku wrote:
    On 2023-11-16, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> wrote:
    I am unsure where from that discussion of implementation came. The
    *spec* permits this (signalling without holding the mutex), just warns >>>> that a predictable scheduling behavior cannot be achieved then.

    The spec is supposed to tell implementors what to make.

    Loose wording like this is dangerous. An implementor is free to
    interpret it like this: whenever a program calls pthread_cond_signal,
    and it so happens that the calling threads owns no mutex whatsoever, the >>> implementation is free to set a flag somewhere, based on which it will
    willfully damage the integrity of the scheduling implementation somehow, >>> so that it behaves like crap.

    If your complaint is about "predictable scheduling behavior" not being
    defined in the standard, I understand and sympathize. It could be better
    defined. When POSIX threads were developed, this was kinda obvious
    because RT guys required that and they know what they mean by that
    (meaning -- more or less agree). This is not a good excuse for not
    spelling it in the standard of course.

    See if this old posting of David Butenhof, one of the spec authors may
    clarify the concept:

    https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    When David wrote that, multiprocessor systems were still rare and
    limited to very expensive systems (e.g. Sequent) (and for the most
    part, weren't running real-time code). I was on the X/Open working group for a few years during that time period, and one of my collegues at
    SGI was part of the 1003.4 working group.

    thirty years later, pthread_cond_t is heavily used by non-realtime
    code, where such priority inversions (usually very transient) don't
    matter.

    For the number of cases where pthread_cond_t is used in hard
    real-time code, the appropriate techniques (i.e. holding the mutex
    while signaling the condition) are available.


    Exactly. It depends on what the programmer is trying to accomplish.
    Signaling inside of a mutex locked region will work regardless of
    priority issues, your code will work.

    However, _if_ your goal is not real time and/or enforcing some specific scheduling polity... Then, I would refrain from that as signalling
    outside of the mutex is more efficient in this case.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Fri Nov 17 18:37:35 2023
    On 2023-11-17, Scott Lurndal <scott@slp53.sl.home> wrote: >>https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    When David wrote that, multiprocessor systems were still rare and

    The referenced discussion appears to be from early 2010; maybe
    he was reiterating something from an earlier time?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Sat Nov 18 00:45:56 2023
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-17, Scott Lurndal <scott@slp53.sl.home> wrote: >>>https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    When David wrote that, multiprocessor systems were still rare and

    The referenced discussion appears to be from early 2010; maybe
    he was reiterating something from an earlier time?

    The Posix 1003.4 (.4b - realtime and .4c - threads) work was in the
    mid 90's. At that time, SMP systems were expensive and relatively
    rare. My 2p octane workstation in 1998 was great, but uncommon even
    in SGI engineering.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Sat Nov 18 02:30:41 2023
    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-17, Scott Lurndal <scott@slp53.sl.home> wrote: >>>>https://austin-group-l.opengroup.narkive.com/lKcmfoRI/predictable-scheduling-behavior-in-pthread-cond-broadcast

    When David wrote that, multiprocessor systems were still rare and

    The referenced discussion appears to be from early 2010; maybe
    he was reiterating something from an earlier time?

    The Posix 1003.4 (.4b - realtime and .4c - threads) work was in the
    mid 90's. At that time, SMP systems were expensive and relatively
    rare. My 2p octane workstation in 1998 was great, but uncommon even
    in SGI engineering.

    In 1995, I had a PC with a consumer-grade dual Pentium 100 ASUS motherboard, running Linux 1.3.x, FWIW.

    Two parallel gcc compilations were a whopping 30% faster than one!

    :)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Bonita Montero on Fri Nov 17 21:16:29 2023
    Bonita Montero wrote:
    Am 15.11.2023 um 03:10 schrieb Pavel:
    Bonita Montero wrote:
    Am 09.11.2023 um 06:48 schrieb Chris M. Thomasson:
    On 11/8/2023 9:45 PM, Bonita Montero wrote:
    Am 09.11.2023 um 06:44 schrieb Chris M. Thomasson:

    Wait morphing is not in the realm of the compiler. It's in the
    kernel.

    Read this:
    https://stackoverflow.com/questions/45163701/which-os-platforms-implement-wait-morphing-optimization


    Wait morphing can be highly beneficial.

    Wait morphing isn't necessary under Win32 since you can wait
    for the mutexe's binary semaphore and for the condvar's counting
    semaphore in one step with WaitForMultipleObjects. Unfortunately
    there's nothing under Linux like that.

    Not true. A Linux program can wait for any number of eventfd
    semaphores that can be used as either binary or a counting one.

    An eventfd can be a semaphore, but only one semaphore and not a set
    like SysV semaphores. You can wait for multiple semaphores with poll
    or select, but a change on one semaphore releases the waiting thread
    whereas with Win32 you can wait for all conditions to be met at once.

    There is no system call in UNIX to wait for many conditions to be met at
    once; but I do not see why it would be useful.

    E.g., if there are several vars initialized to 0 and it is required to
    wake up a consumer thread when they all are made positive (or >5 or
    whatever) by some producer thread or threads, a producer thread that
    sets the last variable to the needed value can signal a single condvar
    and the consumer thread will wake up to all conditions met at once.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 18 04:06:23 2023
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Bonita Montero on Sat Nov 18 12:53:30 2023
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 18 20:04:32 2023
    Am 18.11.2023 um 18:53 schrieb Pavel:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    If you want to substitute a semaphore set with two eventfd()s you'd
    should at best wait for both signalled together to avoid the discussed
    double kernel call. As you this isn't possible with poll() or wait()
    you can stick with normal posix semaphores, which do the same. The
    best would be if you signal the waiting thread with a Posix signal,
    then you can awake the thread upon an arbitrary state. I gues that's
    what the glibc does, or I don't know another way to have only one
    kernel voluntary context switch with pthread_cond_wait.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Pavel on Sat Nov 18 19:24:35 2023
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Sat Nov 18 12:46:25 2023
    On 11/18/2023 9:53 AM, Pavel wrote:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    select?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Sat Nov 18 20:29:51 2023
    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    Also this:

    while (!this_condition() && !that_condition() && !other_condition())
    pthread_cond_wait(&cond, &mutex);

    One condition variable can be used to wait for any subset of the
    actual conditions over the shared variables protected by the mutex.

    When I switched to a Win32 development job in the mid 1990's, it amused
    me that Win32 had all these stupid things like WaitForMultipleObjects
    (with a weird 64 limit), instead of just providing condition variables.

    Today, Microsoft provides condition variables.

    Modules of a large program should use callbacks to notify that something
    is ready. That's the most flexible API. The caller can build proper asynchronous control flows around that, or bind the callback to some
    signaling object according to how they see fit.

    Say I have a module that works with three other modules that can
    spontaneously make some condition ready. If they all provide a callback,
    I can set a flag in the callback and broadcast or signal a condition
    variable. Then I can have threads wait for all three things to be
    ready, or any one of the three things.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Chris M. Thomasson on Sat Nov 18 21:47:35 2023
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/18/2023 9:53 AM, Pavel wrote:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    select?

    I took Pavel to mean 'wait for all conditions to be true' rather than
    'wait for one or more conditions to be true'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Sat Nov 18 21:46:56 2023
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met >>>>> at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at >>>once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    Also this:

    while (!this_condition() && !that_condition() && !other_condition())
    pthread_cond_wait(&cond, &mutex);

    One condition variable can be used to wait for any subset of the
    actual conditions over the shared variables protected by the mutex.

    When I switched to a Win32 development job in the mid 1990's, it amused
    me that Win32 had all these stupid things like WaitForMultipleObjects
    (with a weird 64 limit), instead of just providing condition variables.

    IIRC, that's a concept imported from VMS into NT by DC.

    The Burroughs MCP had a 'complex wait' system call which could wait
    for the occurance of any of a number of conditions (card reader ready,
    tape unit ready, input ready from datacomm station, core-to-core
    communication from cooperating process, inter-processor persistent
    storage queue item availability, time delay, disk seek complete,
    network data present, etc.

    But it was a wait for any, not wait for all.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Sat Nov 18 14:02:50 2023
    On 11/18/2023 1:47 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/18/2023 9:53 AM, Pavel wrote:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met >>>>> at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    select?

    I took Pavel to mean 'wait for all conditions to be true' rather than
    'wait for one or more conditions to be true'.


    Ahhh. I missed that. Sorry everybody! ;^o

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Scott Lurndal on Sat Nov 18 20:09:26 2023
    Scott Lurndal wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    Rendezvous.
    Is it even useful? I got familiar with it first in late 80s while
    learning Ada but never needed it since, even as a concept.
    e.g. pthread_barrier_wait.

    It seems to work well as it is, my feeling is that implementing it in
    user space on top of some "wait_for_all_xxxs" (which is on itself too high-level for my taste) would not be efficient.

    After giving it some thought, I think I could write a reasonable
    implementation with 1 mutex, 1 condvar and 2 ints (unless some
    "predictable scheduling policy" is required -- but the implementation
    with "wait_for_all_xxxs" should have issue with it as well).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Chris M. Thomasson on Sat Nov 18 20:20:18 2023
    Chris M. Thomasson wrote:
    On 11/18/2023 1:47 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/18/2023 9:53 AM, Pavel wrote:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met >>>>>> at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    select?

    I took Pavel to mean 'wait for all conditions to be true' rather than
    'wait for one or more conditions to be true'.


    Ahhh. I missed that. Sorry everybody! ;^o
    NP, Scott is correct, this was about "wait for many conditions to be met
    at once" that Bonita missed on Linux for some reason I still cannot get
    or relate to.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pavel@21:1/5 to Kaz Kylheku on Sat Nov 18 20:18:18 2023
    Kaz Kylheku wrote:
    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be met >>>>> at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at
    once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    Also this:

    while (!this_condition() && !that_condition() && !other_condition())
    pthread_cond_wait(&cond, &mutex);

    I had to solve similar tasks once in a while, would usually just have a
    count of conditions left or a mask of condition wanted as bits and the
    thread satisfying a condition would only signal once count is zero or
    the mask covers all conditions, respectively to
    - avoid unnecessary wake-ups;
    - let waiters check that count or mask instead of doing multiple checks



    One condition variable can be used to wait for any subset of the
    actual conditions over the shared variables protected by the mutex.

    When I switched to a Win32 development job in the mid 1990's, it amused
    me that Win32 had all these stupid things like WaitForMultipleObjects
    (with a weird 64 limit), instead of just providing condition variables.

    Today, Microsoft provides condition variables.

    Modules of a large program should use callbacks to notify that something
    is ready. That's the most flexible API. The caller can build proper asynchronous control flows around that, or bind the callback to some signaling object according to how they see fit.

    Say I have a module that works with three other modules that can spontaneously make some condition ready. If they all provide a callback,
    I can set a flag in the callback and broadcast or signal a condition variable. Then I can have threads wait for all three things to be
    ready, or any one of the three things.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Sun Nov 19 05:02:25 2023
    On 2023-11-19, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 18.11.2023 um 20:24 schrieb Scott Lurndal:

    e.g. pthread_barrier_wait.

    pthread_barrier_wait is for multiple threads joining on one event,
    not one thread waiting for multiple events as discussed.

    That is untrue; there are effectively N events in a barrier:

    - thread 1 has arrived at the barrier
    - thread 2 has arrived at the barrier
    ...
    - thread N has arrived at the barrier

    When all these events have occurred, then all threads at the barrier are released (and an indication is given to one of them that it is the
    special "serial thread").

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 19 05:58:24 2023
    Am 18.11.2023 um 20:24 schrieb Scott Lurndal:

    e.g. pthread_barrier_wait.

    pthread_barrier_wait is for multiple threads joining on one event,
    not one thread waiting for multiple events as discussed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 19 07:00:51 2023
    Am 19.11.2023 um 06:02 schrieb Kaz Kylheku:
    On 2023-11-19, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 18.11.2023 um 20:24 schrieb Scott Lurndal:

    e.g. pthread_barrier_wait.

    pthread_barrier_wait is for multiple threads joining on one event,
    not one thread waiting for multiple events as discussed.

    That is untrue; there are effectively N events in a barrier:

    No, a barrier internally consists of an atomic which each thread
    decrements and if it wasn't the last thread decrementing the atomic
    it waits for a semaphore, a.k.a. event, which is signalled by the
    last thread which decremented the atomic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Sun Nov 19 06:08:25 2023
    On 2023-11-19, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 19.11.2023 um 06:02 schrieb Kaz Kylheku:
    On 2023-11-19, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 18.11.2023 um 20:24 schrieb Scott Lurndal:

    e.g. pthread_barrier_wait.

    pthread_barrier_wait is for multiple threads joining on one event,
    not one thread waiting for multiple events as discussed.

    That is untrue; there are effectively N events in a barrier:

    No, a barrier internally consists of an atomic which each thread
    decrements

    That's an implementation detail. Because the events are one-shots we can
    just use a counter to represent the state that we need for the barrier
    to be able to conclude that all events have occurred.

    Each "thread P is waiting for barrier" event is a one shot, because
    once a thread is waiting, that fact stays latched.

    If the waiting threads could get nervous and leave the barrier
    before it activates, then a counter could not be used; there would
    have to be a set representation like a bitmask.

    Rest unread.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 19 12:05:21 2023
    Am 19.11.2023 um 07:08 schrieb Kaz Kylheku:

    That's an implementation detail. ...

    That's how it works.
    So from the scheduling / kernel perspective there's only one event.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Sun Nov 19 12:11:58 2023
    On 11/19/2023 12:08 PM, Chris M. Thomasson wrote:
    On 11/19/2023 3:05 AM, Bonita Montero wrote:
    Am 19.11.2023 um 07:08 schrieb Kaz Kylheku:

    That's an implementation detail. ...

    That's how it works.
    So from the scheduling / kernel perspective there's only one event.



    I suspect that you are unfamiliar with how WaitForMultipleObjects
    works... I remember a test for 50,000 concurrent connections, that was mentioned by Microsoft. It was testing events vs IOCP. Did you know that
    they recommended altering the indexes of the events waited for by WaitForMultipleObjects? I remember it, is was a long time ago, 2001 ish, iirc.

    Iirc, it was recommended to basically randomize, or shift the indexes.
    God damn, I need to find that old post on msdn. Again, iirc, if we did
    not do this, some events could starve in the server experiment.

    The funny part is that the event based model performed pretty good, not
    as scalable as IOCP, however, IOCP did not completely blow it out of the water wrt performance and throughput.

    After I read that msdn post, I basically created a proxy server. One
    using events, and another using IOCP. IOCP beats it, but did not
    slaughter it at that time. I found that to be interesting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sun Nov 19 12:08:54 2023
    On 11/19/2023 3:05 AM, Bonita Montero wrote:
    Am 19.11.2023 um 07:08 schrieb Kaz Kylheku:

    That's an implementation detail. ...

    That's how it works.
    So from the scheduling / kernel perspective there's only one event.



    I suspect that you are unfamiliar with how WaitForMultipleObjects
    works... I remember a test for 50,000 concurrent connections, that was mentioned by Microsoft. It was testing events vs IOCP. Did you know that
    they recommended altering the indexes of the events waited for by WaitForMultipleObjects? I remember it, is was a long time ago, 2001 ish,
    iirc.

    Iirc, it was recommended to basically randomize, or shift the indexes.
    God damn, I need to find that old post on msdn. Again, iirc, if we did
    not do this, some events could starve in the server experiment.

    The funny part is that the event based model performed pretty good, not
    as scalable as IOCP, however, IOCP did not completely blow it out of the
    water wrt performance and throughput.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Sun Nov 19 12:17:21 2023
    On 11/19/2023 12:11 PM, Chris M. Thomasson wrote:
    On 11/19/2023 12:08 PM, Chris M. Thomasson wrote:
    On 11/19/2023 3:05 AM, Bonita Montero wrote:
    Am 19.11.2023 um 07:08 schrieb Kaz Kylheku:

    That's an implementation detail. ...

    That's how it works.
    So from the scheduling / kernel perspective there's only one event.



    I suspect that you are unfamiliar with how WaitForMultipleObjects
    works... I remember a test for 50,000 concurrent connections, that was
    mentioned by Microsoft. It was testing events vs IOCP. Did you know
    that they recommended altering the indexes of the events waited for by
    WaitForMultipleObjects? I remember it, is was a long time ago, 2001
    ish, iirc.

    Iirc, it was recommended to basically randomize, or shift the indexes.
    God damn, I need to find that old post on msdn. Again, iirc, if we did
    not do this, some events could starve in the server experiment.

    The funny part is that the event based model performed pretty good,
    not as scalable as IOCP, however, IOCP did not completely blow it out
    of the water wrt performance and throughput.

    After I read that msdn post, I basically created a proxy server. One
    using events, and another using IOCP. IOCP beats it, but did not
    slaughter it at that time. I found that to be interesting.

    That's way back when I remember using AIO for the HTTP proxy server over
    on Linux and compared it to IOCP. Time flies!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 20 13:26:43 2023
    Am 19.11.2023 um 21:08 schrieb Chris M. Thomasson:

    I suspect that you are unfamiliar with how WaitForMultipleObjects
    works... ...

    Absolutely not, but with WFMO you'd need a lot of semaphores or events
    whereas my solution needs one semaphore and you'd be limited to MAXIMUM _WAIT_OBJECTS == 64 threads which can join the barrier.
    And why do you think I didn't know that ? I've shown that I used that
    with my monitor and you even didn't have to look at the source since
    I've explained that I'm using WFMO multiple times.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Mon Nov 20 13:12:41 2023
    On 11/18/2023 5:20 PM, Pavel wrote:
    Chris M. Thomasson wrote:
    On 11/18/2023 1:47 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/18/2023 9:53 AM, Pavel wrote:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to be >>>>>>> met
    at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met at >>>>> once can be useful for?

    select?

    I took Pavel to mean 'wait for all conditions to be true' rather than
    'wait for one or more conditions to be true'.


    Ahhh. I missed that. Sorry everybody! ;^o
    NP, Scott is correct, this was about "wait for many conditions to be met
    at once" that Bonita missed on Linux for some reason I still cannot get
    or relate to.

    It guess Bonita does not know that a predicate for a condition variable
    can contain many conditions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 20 12:18:15 2023
    On 11/20/2023 4:26 AM, Bonita Montero wrote:
    Am 19.11.2023 um 21:08 schrieb Chris M. Thomasson:

    I suspect that you are unfamiliar with how WaitForMultipleObjects
    works... ...

    Absolutely not, but with WFMO you'd need a lot of semaphores or events whereas my solution needs one semaphore and you'd be limited to MAXIMUM _WAIT_OBJECTS == 64 threads which can join the barrier.
    And why do you think I didn't know that ? I've shown that I used that
    with my monitor and you even didn't have to look at the source since
    I've explained that I'm using WFMO multiple times.


    I was just thinking about the case that did not have to wait on all the
    events your are passing into WFMO to be in a signaled state. That old
    50,000 connection test (event vs. iocp) required us to shift their
    positions in the array in order to get around some starvation issues...

    Did you read that article on MSDN as well? Iirc, its around 20 years old.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Tue Nov 21 10:25:22 2023
    Am 20.11.2023 um 21:18 schrieb Chris M. Thomasson:

    Did you read that article on MSDN as well? Iirc, its around 20 years old.

    Has WFMO ever changed ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Kaz Kylheku on Fri Nov 24 01:14:42 2023
    On Sat, 18 Nov 2023 20:29:51 -0000 (UTC)
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:

    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to
    be met at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met
    at once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    Also this:

    while (!this_condition() && !that_condition() &&
    !other_condition()) pthread_cond_wait(&cond, &mutex);

    One condition variable can be used to wait for any subset of the
    actual conditions over the shared variables protected by the mutex.

    When I switched to a Win32 development job in the mid 1990's, it
    amused me that Win32 had all these stupid things like
    WaitForMultipleObjects (with a weird 64 limit), instead of just
    providing condition variables.


    1. WaitForMultipleObjects() can wait on any mix of objects
    2. More importantly, it can wait on wake up events originated both
    inside the process and outside.


    Today, Microsoft provides condition variables.


    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Modules of a large program should use callbacks to notify that
    something is ready. That's the most flexible API. The caller can
    build proper asynchronous control flows around that, or bind the
    callback to some signaling object according to how they see fit.

    Say I have a module that works with three other modules that can spontaneously make some condition ready. If they all provide a
    callback, I can set a flag in the callback and broadcast or signal a condition variable. Then I can have threads wait for all three
    things to be ready, or any one of the three things.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Thu Nov 23 21:38:59 2023
    On 11/21/2023 1:25 AM, Bonita Montero wrote:
    Am 20.11.2023 um 21:18 schrieb Chris M. Thomasson:

    Did you read that article on MSDN as well? Iirc, its around 20 years old.

    Has WFMO ever changed ?


    No, I don't think so.. The interesting aspect was about trying to avoid starvation the the wait array wrt the IOCP vs Event contest. So, we were advised to randomize and/or shift the positions wrt the event side wrt WFMO.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Michael S on Thu Nov 23 21:35:53 2023
    On 11/23/2023 3:14 PM, Michael S wrote:
    On Sat, 18 Nov 2023 20:29:51 -0000 (UTC)
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:

    On 2023-11-18, Scott Lurndal <scott@slp53.sl.home> wrote:
    Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> writes:
    Bonita Montero wrote:
    Am 18.11.2023 um 03:16 schrieb Pavel:

    There is no system call in UNIX to wait for many conditions to
    be met at once; but I do not see why it would be useful.

    poll() and wait() wait for at least one condition to be met.
    what does it have to do with what I wrote?

    Again, what a system call that waits for many conditions to be met
    at once can be useful for?

    Rendezvous.

    e.g. pthread_barrier_wait.

    Also this:

    while (!this_condition() && !that_condition() &&
    !other_condition()) pthread_cond_wait(&cond, &mutex);

    One condition variable can be used to wait for any subset of the
    actual conditions over the shared variables protected by the mutex.

    When I switched to a Win32 development job in the mid 1990's, it
    amused me that Win32 had all these stupid things like
    WaitForMultipleObjects (with a weird 64 limit), instead of just
    providing condition variables.


    1. WaitForMultipleObjects() can wait on any mix of objects
    2. More importantly, it can wait on wake up events originated both
    inside the process and outside.


    Today, Microsoft provides condition variables.


    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Fwiw, check this out:

    https://sourceware.org/pthreads-win32/

    ;^)


    Modules of a large program should use callbacks to notify that
    something is ready. That's the most flexible API. The caller can
    build proper asynchronous control flows around that, or bind the
    callback to some signaling object according to how they see fit.

    Say I have a module that works with three other modules that can
    spontaneously make some condition ready. If they all provide a
    callback, I can set a flag in the callback and broadcast or signal a
    condition variable. Then I can have threads wait for all three
    things to be ready, or any one of the three things.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 24 08:56:13 2023
    Am 24.11.2023 um 06:38 schrieb Chris M. Thomasson:

    No, I don't think so.. The interesting aspect was about trying to avoid starvation the the wait array wrt the IOCP vs Event contest. So, we were advised to randomize and/or shift the positions wrt the event side wrt
    WFMO.

    You officially can't wait for an IOCP handle with WFMO, although this
    actually works with my Windows 11 computer. MS should document that and
    make a statement since which Windows version this actually works. This
    isn't a strong requirement but it would be nice to wait for IOCPs with
    WFMO in some cases.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 24 08:59:27 2023
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish mutexes and condition variables since C++11 are more convenient to use.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Fri Nov 24 01:22:22 2023
    On 11/23/2023 11:56 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:38 schrieb Chris M. Thomasson:

    No, I don't think so.. The interesting aspect was about trying to
    avoid starvation the the wait array wrt the IOCP vs Event contest. So,
    we were advised to randomize and/or shift the positions wrt the event
    side wrt WFMO.

    You officially can't wait for an IOCP handle with WFMO, although this actually works with my Windows 11 computer. MS should document that and
    make a statement since which Windows version this actually works. This
    isn't a strong requirement but it would be nice to wait for IOCPs with
    WFMO in some cases.


    Humm... You should be using:

    https://learn.microsoft.com/en-us/windows/win32/fileio/getqueuedcompletionstatusex-func

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Fri Nov 24 01:17:53 2023
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish mutexes and condition variables since C++11 are more convenient to use.





    Have you ever wrapped up a C API in C++ using RAII before?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 24 10:25:17 2023
    Am 24.11.2023 um 10:17 schrieb Chris M. Thomasson:

    Have you ever wrapped up a C API in C++ using RAII before?

    Sometimes, but mostly I don't write a dedicated class for that but use
    my own invoke_on_destruct class which is similar to the experimental# scope_exit: https://en.cppreference.com/w/cpp/experimental/scope_exit

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Chris M. Thomasson on Fri Nov 24 15:59:03 2023
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish
    mutexes and condition variables since C++11 are more convenient to use.





    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large
    2) It makes reasoning about the synchronization code and
    potential nested locking more difficult.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Fri Nov 24 19:10:09 2023
    Am 24.11.2023 um 10:22 schrieb Chris M. Thomasson:
    On 11/23/2023 11:56 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:38 schrieb Chris M. Thomasson:

    No, I don't think so.. The interesting aspect was about trying to
    avoid starvation the the wait array wrt the IOCP vs Event contest.
    So, we were advised to randomize and/or shift the positions wrt the
    event side wrt WFMO.

    You officially can't wait for an IOCP handle with WFMO, although this
    actually works with my Windows 11 computer. MS should document that and
    make a statement since which Windows version this actually works. This
    isn't a strong requirement but it would be nice to wait for IOCPs with
    WFMO in some cases.


    Humm... You should be using:

    https://learn.microsoft.com/en-us/windows/win32/fileio/getqueuedcompletionstatusex-func

    Why do you think I don't know GetQueuedCompletionStatus() ?
    I said that it would be nice to do a WFMO on a IOCP in addition to that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Fri Nov 24 18:33:08 2023
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not
    ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>> mutexes and condition variables since C++11 are more convenient to use.





    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    2) It makes reasoning about the synchronization code and
    potential nested locking more difficult.

    It's just a syntactic sugar to avoid forgetting to unlock.

    The thing is, you need a complementary unlocker to
    avoid the nesting:

    {
    mutex_locker m(obj);

    // ...

    if (obj.situation()) {
    mutex_unlocker u(obj); // temporarily give up mutex
    other_obj.method(); // this can call us back again
    }
    }


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Kaz Kylheku on Fri Nov 24 15:39:54 2023
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>>> mutexes and condition variables since C++11 are more convenient to use. >>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    DING!!! :^)



    2) It makes reasoning about the synchronization code and
    potential nested locking more difficult.

    It's just a syntactic sugar to avoid forgetting to unlock.

    The thing is, you need a complementary unlocker to
    avoid the nesting:

    {
    mutex_locker m(obj);

    // ...

    if (obj.situation()) {
    mutex_unlocker u(obj); // temporarily give up mutex
    other_obj.method(); // this can call us back again
    }
    }



    :^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Fri Nov 24 15:43:03 2023
    On 11/24/2023 10:10 AM, Bonita Montero wrote:
    Am 24.11.2023 um 10:22 schrieb Chris M. Thomasson:
    On 11/23/2023 11:56 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:38 schrieb Chris M. Thomasson:

    No, I don't think so.. The interesting aspect was about trying to
    avoid starvation the the wait array wrt the IOCP vs Event contest.
    So, we were advised to randomize and/or shift the positions wrt the
    event side wrt WFMO.

    You officially can't wait for an IOCP handle with WFMO, although this
    actually works with my Windows 11 computer. MS should document that and
    make a statement since which Windows version this actually works. This
    isn't a strong requirement but it would be nice to wait for IOCPs with
    WFMO in some cases.


    Humm... You should be using:

    https://learn.microsoft.com/en-us/windows/win32/fileio/getqueuedcompletionstatusex-func

    Why do you think I don't know GetQueuedCompletionStatus() ?

    Btw, I said:

    GetQueuedCompletionStatusEx

    NOT

    GetQueuedCompletionStatus

    :^)

    I said that it would be nice to do a WFMO on a IOCP in addition to that.

    How would that even work? You do not need to create an event per
    connection in IOCP model. IOCP is a different model that is not
    compatible with WFMO.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Chris M. Thomasson on Sat Nov 25 00:08:31 2023
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>>>> mutexes and condition variables since C++11 are more convenient to use. >>>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    DING!!! :^)

    No, not really. It's not always that
    easy, and sometimes programmers even use 'goto'.

    That's certainly no more readable than

    pthread_lock(obj);

    do something

    pthread_unlock(obj);

    I'd argue the RAII solution is less readable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Fri Nov 24 16:27:54 2023
    On 11/24/2023 4:08 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>>>>> mutexes and condition variables since C++11 are more convenient to use. >>>>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    DING!!! :^)

    No, not really. It's not always that
    easy, and sometimes programmers even use 'goto'.

    That's certainly no more readable than

    pthread_lock(obj);

    do something

    pthread_unlock(obj);

    I'd argue the RAII solution is less readable.

    RAII can come in handy. I don't think its all that bad. Humm... _______________________
    {
    mutex_raii_lock(my_lock);
    }
    _______________________

    vs:
    _______________________
    my_lock.lock();
    my_lock.unlock();
    _______________________

    Btw, wrt C++, RAII tends to work rather well with exceptions... ;^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 25 13:04:01 2023
    Am 25.11.2023 um 00:43 schrieb Chris M. Thomasson:

    How would that even work? You do not need to create an event per
    connection in IOCP model. IOCP is a different model that is not
    compatible with WFMO.

    You'd have to ask GQCS after you waited. Actually WFMO works
    on a IOCP für my Windows 11 PC, but this isn't documented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 25 13:12:46 2023
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Fred. Zwarts@21:1/5 to All on Sat Nov 25 13:02:42 2023
    Op 25.nov.2023 om 01:08 schreef Scott Lurndal:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>>>>> mutexes and condition variables since C++11 are more convenient to use. >>>>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    DING!!! :^)

    No, not really. It's not always that
    easy, and sometimes programmers even use 'goto'.

    That's certainly no more readable than

    pthread_lock(obj);

    do something

    pthread_unlock(obj);

    I'd argue the RAII solution is less readable.

    In many cases it is not that simple.
    It is more like:

    {
    mutex_locker m(obj);
    do something
    if (....) continue;
    if (....) break;
    if (...) return;
    do something else
    }

    vs.:

    pthread_lock(obj);
    {
    do something
    if (...) {
    pthread_unlock(obj);
    continue;
    }
    if (...) {
    pthread_unlock(obj);
    break;
    }
    if (....) {
    pthread_unlock(obj);
    return;
    }
    do something else
    } catch (...) {
    pthread_unlock(obj);
    throw;
    }
    pthread_unlock(obj);

    Personally I find the first way more readable and easier to maintain.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Sat Nov 25 13:58:31 2023
    On 24/11/2023 19:33, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish >>>> mutexes and condition variables since C++11 are more convenient to use. >>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    Yes.


    {
    mutex_locker m(obj);


    }

    2) It makes reasoning about the synchronization code and
    potential nested locking more difficult.

    It's just a syntactic sugar to avoid forgetting to unlock.

    That makes it sound like it is possible to use RAII locks to avoid
    thinking too much, and that's a bad attitude. I would rather say that
    RAII lockers can be useful to keep code clearer, and make the locked
    section obvious with less clutter. It is still just as important to
    think carefully about where the locks go!


    The thing is, you need a complementary unlocker to
    avoid the nesting:

    {
    mutex_locker m(obj);

    // ...

    if (obj.situation()) {
    mutex_unlocker u(obj); // temporarily give up mutex
    other_obj.method(); // this can call us back again
    }
    }



    With unlockers, you are getting complicated again, and it's hard to
    understand and reason about the locks. They can be useful, but rarely.

    IME, RAII locks are sometimes a better choice, while other times free
    lock and unlock functions are better. There is no "one size fits all"
    answer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Sat Nov 25 18:09:49 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 25.11.2023 um 18:28 schrieb Scott Lurndal:

    I've used both, but in code (bare metal os/hv) compiled with
    -fno-exceptions the following is preferable to RAII.

    pthread_lock(obj);

    if (xxx) goto unlock;

    do something

    if (yyy) goto unlock;

    do something else

    unlock:
    pthread_unlock(obj);

    Why do you disable exceptions with code that doesn't throw ?

    To prevent the code from ever throwing, of course and to ensure
    that no exception infrastructure gets generated into the object
    files.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Nov 25 18:48:55 2023
    Am 25.11.2023 um 18:28 schrieb Scott Lurndal:

    I've used both, but in code (bare metal os/hv) compiled with
    -fno-exceptions the following is preferable to RAII.

    pthread_lock(obj);

    if (xxx) goto unlock;

    do something

    if (yyy) goto unlock;

    do something else

    unlock:
    pthread_unlock(obj);

    Why do you disable exceptions with code that doesn't throw ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bonita Montero on Sat Nov 25 18:10:37 2023
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Fred. Zwarts on Sat Nov 25 17:28:18 2023
    "Fred. Zwarts" <F.Zwarts@HetNet.nl> writes:
    Op 25.nov.2023 om 01:08 schreef Scott Lurndal:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were not >>>>>>>>> ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the RAII-ish
    mutexes and condition variables since C++11 are more convenient to use. >>>>>>>




    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated. Amongst the reasons:
    1) The critical sections usually end up being far to large

    Use braces?

    {
    mutex_locker m(obj);


    }

    DING!!! :^)

    No, not really. It's not always that
    easy, and sometimes programmers even use 'goto'.

    That's certainly no more readable than

    pthread_lock(obj);

    do something

    pthread_unlock(obj);

    I'd argue the RAII solution is less readable.

    In many cases it is not that simple.
    It is more like:

    {
    mutex_locker m(obj);
    do something
    if (....) continue;
    if (....) break;
    if (...) return;
    do something else
    }

    vs.:

    pthread_lock(obj);
    {
    do something
    if (...) {
    pthread_unlock(obj);
    continue;
    }
    if (...) {
    pthread_unlock(obj);
    break;
    }
    if (....) {
    pthread_unlock(obj);
    return;
    }
    do something else
    } catch (...) {
    pthread_unlock(obj);
    throw;
    }
    pthread_unlock(obj);

    Personally I find the first way more readable and easier to maintain.

    I'd argue that the critical region may be too large if all those
    checks are required within.

    I've used both, but in code (bare metal os/hv) compiled with
    -fno-exceptions the following is preferable to RAII.

    pthread_lock(obj);

    if (xxx) goto unlock;

    do something

    if (yyy) goto unlock;

    do something else

    unlock:
    pthread_unlock(obj);

    six of one, half dozen of the other. This solution doesn't have
    magic behavior and can more easily be reasoned to be correct.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Sat Nov 25 19:30:03 2023
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Sat Nov 25 14:57:49 2023
    On 11/25/2023 11:30 AM, Scott Lurndal wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    It can create a dog with different fleas in some cases.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Sat Nov 25 14:47:28 2023
    On 11/24/2023 4:27 PM, Chris M. Thomasson wrote:
    On 11/24/2023 4:08 PM, Scott Lurndal wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/24/2023 10:33 AM, Kaz Kylheku wrote:
    On 2023-11-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 11/23/2023 11:59 PM, Bonita Montero wrote:
    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    On 11/23/2023 3:14 PM, Michael S wrote:

    Which, I would guess, never used by 99.9% of programs that were >>>>>>>>> not
    ported to Windows from some other platform.

    Fwiw, check this out:
    https://sourceware.org/pthreads-win32/

    Am 24.11.2023 um 06:35 schrieb Chris M. Thomasson:

    That's an unnecessary discussion for a C++ programmer since the
    RAII-ish
    mutexes and condition variables since C++11 are more convenient
    to use.





    Have you ever wrapped up a C API in C++ using RAII before?

    I generally consider using RAII for synchronization primitives
    to be counterindicated.    Amongst the reasons:
       1) The critical sections usually end up being far to large

    Use braces?

         {
            mutex_locker m(obj);


         }

    DING!!! :^)

    No, not really.  It's not always that
    easy, and sometimes programmers even use 'goto'.

    That's certainly no more readable than

        pthread_lock(obj);

        do something

        pthread_unlock(obj);

    I'd argue the RAII solution is less readable.

    RAII can come in handy. I don't think its all that bad. Humm... _______________________
    {
       mutex_raii_lock(my_lock);
    ^^^^^^^^^^^^^

    OOPS!
    mutex_raii_lock lock(my_lock);


    }
    _______________________

    vs:
    _______________________
    my_lock.lock();
    my_lock.unlock();
    _______________________

    Btw, wrt C++, RAII tends to work rather well with exceptions... ;^)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sat Nov 25 15:06:06 2023
    On 11/25/2023 4:04 AM, Bonita Montero wrote:
    Am 25.11.2023 um 00:43 schrieb Chris M. Thomasson:

    How would that even work? You do not need to create an event per
    connection in IOCP model. IOCP is a different model that is not
    compatible with WFMO.

    You'd have to ask GQCS after you waited.

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    Actually WFMO works
    on a IOCP für my Windows 11 PC, but this isn't documented.

    So your hack can break at any time? Keep in mind that I said GQCSEX...
    :^) Its more efficient that GQCS. Actually, have you ever read a paper
    on server design that used cohort scheduling?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Scott Lurndal on Sun Nov 26 01:29:40 2023
    On Sat, 25 Nov 2023 19:30:03 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    RAII is horrible, but unfortunately necessary in big undisciplined C++ projects.
    The root bug in C++ language design are constructors that can fail, but
    can't return error code.
    That problem begot exceptions. And the presence of exceptions begot
    RAII. RAII is bad, but less bad than other solutions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Michael S on Sat Nov 25 15:56:03 2023
    On 11/25/2023 3:29 PM, Michael S wrote:
    On Sat, 25 Nov 2023 19:30:03 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    RAII is horrible, but unfortunately necessary in big undisciplined C++ projects.

    Horrible? It's not that bad.



    The root bug in C++ language design are constructors that can fail, but
    can't return error code.
    That problem begot exceptions. And the presence of exceptions begot
    RAII. RAII is bad, but less bad than other solutions.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 26 11:35:51 2023
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    So your hack can break at any time? Keep in mind that I said GQCSEX...
    :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Michael S on Sun Nov 26 13:54:12 2023
    On 26/11/2023 00:29, Michael S wrote:
    On Sat, 25 Nov 2023 19:30:03 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:

    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    RAII is horrible, but unfortunately necessary in big undisciplined C++ projects.
    The root bug in C++ language design are constructors that can fail, but
    can't return error code.

    That's easily solved in cases like this - write constructors that can't
    fail.

    In the kind of systems I work with, something like acquiring a mutex
    cannot fail unless there is a major system failure that renders it
    unsafe to run anything - the system then turns off all the machinery it controls, logs everything it can, and alerts the user to the failure.
    That applies equally to a free-standing "lock" function or an RAII
    constructor solution. There is no need for exceptions, error codes, or anything of that sort.

    That problem begot exceptions. And the presence of exceptions begot
    RAII. RAII is bad, but less bad than other solutions.


    I am not a fan of exceptions in low-level code or control code. They
    are fine in high-level PC stuff when dealing with things that can
    reasonably fail but where you don't want to bother with all the details
    all the time - such as a constructor for opening a handle to a database.
    (I mostly use Python for that kind of thing anyway, but that's just
    personal choice.)

    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier, and RAII is just another useful tool.
    (And you can compile with exceptions fully disabled.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 26 14:38:58 2023
    Am 26.11.2023 um 13:54 schrieb David Brown:

    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier, and RAII is just another useful tool.
    (And you can compile with exceptions fully disabled.)

    With exceptions and RAII failures are also very easy to handle because
    you handle them only at a few places and not where they happen or in
    the call levels between.
    Avoiding exceptions looks hard to me because the runtime can throw a
    lot of exceptions, mostly bad_alloc()s or system_error()s.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bonita Montero on Sun Nov 26 16:28:31 2023
    On 26/11/2023 14:38, Bonita Montero wrote:
    Am 26.11.2023 um 13:54 schrieb David Brown:

    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier, and RAII is just another useful
    tool. (And you can compile with exceptions fully disabled.)

    With exceptions and RAII failures are also very easy to handle because
    you handle them only at a few places and not where they happen or in
    the call levels between.

    No, exceptions do not make things easier - they make them /different/. Sometimes it might be helpful to move failure handling to a different
    part of the code, sometimes it makes things harder. Sometimes it is
    best to have a failure handling solution that does not care what can
    fail, or how it can fail, and only think about failures when you have a
    way to deal with them (that's exceptions). Sometimes it is best to be
    entirely sure about wait failures can occur at the point of failure, and
    deal with them or pass them on explicitly (that's error returns,
    std::expect, railway programming, etc.). Sometimes it is best to ensure failure is not possible within the scope of the software, so that no
    failure handling is needed (or nothing beyond abort(), kernel panic, etc.)

    The only thing you can be sure about with failure handling is that
    anyone who says they know the perfect answer, or that their preferred
    method is always far better than others, is wrong.

    Avoiding exceptions looks hard to me because the runtime can throw a
    lot of exceptions, mostly bad_alloc()s or system_error()s.


    Any code that sees a "bad_alloc" exception is already borked. It is not failing because of the exception, it is failing because of the bad
    programming.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sun Nov 26 18:51:14 2023
    Am 26.11.2023 um 16:28 schrieb David Brown:

    No, exceptions do not make things easier - they make them /different/.

    Without exceptions you'd have to deal with return codes at each call
    level; this *is* much more work.

    Sometimes it might be helpful to move failure handling to a different
    part of the code, sometimes it makes things harder.

    In C++ exceptions are mostly used for resource collapse situations, i.e.
    out of memory errors, network errors of filesystem failures. With that exceptions are the most convenient way to handle that. These types of
    errors are handled in most cases.

    Any code that sees a "bad_alloc" exception is already borked.

    With exceptions you can handle such situations in a clean way with
    a small effort.

    It is not failing because of the exception, it is failing because
    of the bad programming.

    bad_alloc doesn't depend on the programming but rather how much memory
    you have. You might ignore bad_alloc for small allocations since they're unlikely to happen, but not for large allocations.
    I don't understand your attitude while programming with C++ because the
    runtime uses exceptions so much.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bonita Montero on Sun Nov 26 20:27:54 2023
    On 26/11/2023 18:51, Bonita Montero wrote:
    Am 26.11.2023 um 16:28 schrieb David Brown:

    No, exceptions do not make things easier - they make them /different/.

    Without exceptions you'd have to deal with return codes at each call
    level; this *is* much more work.

    As I said, anyone who claims their preference is always right, is wrong.


    Sometimes it might be helpful to move failure handling to a different
    part of the code, sometimes it makes things harder.

    In C++ exceptions are mostly used for resource collapse situations, i.e.
    out of memory errors, network errors of filesystem failures.

    These are completely different situations. /Sometimes/ exceptions are a convenient way to handle network or filesystem issues. They are never a
    good way of handling out of memory errors, because out of memory errors
    are caused by failures of the code, not external causes.

    With that
    exceptions are the most convenient way to handle that. These types of
    errors are handled in most cases.


    Back here in the real world, most exceptions are /not/ handled -
    programmers implementing the lower level functions think that exceptions
    are someone else's problem and pass the buck up the line. Programmers implementing the higher level parts don't know that the lower level
    parts might through an exception - they assume functions do what they
    claim to do.

    Used properly, exceptions can be helpful in some situations.
    Unfortunately they are rarely used properly.

    Any code that sees a "bad_alloc" exception is already borked.

    With exceptions you can handle such situations in a clean way with
    a small effort.


    No, you can't. You cannot magically make more memory appear, nor
    magically fix the crappy code that caused a memory error in the first
    place. You are blindly buying into the myth.

    It is not failing because of the exception, it is failing because
    of the bad programming.

    bad_alloc doesn't depend on the programming but rather how much memory
    you have. You might ignore bad_alloc for small allocations since they're unlikely to happen, but not for large allocations.

    If you are trying to make an allocation that is too big for the system,
    you are doing things wrong. Throwing an exception does not help,
    because catching the exception cannot help.

    I don't understand your attitude while programming with C++ because the runtime uses exceptions so much.


    The C++ code I use never throws exceptions. I never have them enabled.
    I don't miss them.

    (I don't use C++ for the kinds of uses where exceptions can be helpful.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bonita Montero on Sun Nov 26 22:55:03 2023
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 26.11.2023 um 13:54 schrieb David Brown:

    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier, and RAII is just another useful tool.
    (And you can compile with exceptions fully disabled.)

    With exceptions and RAII failures are also very easy to handle because
    you handle them only at a few places and not where they happen or in
    the call levels between.
    Avoiding exceptions looks hard to me because the runtime can throw a
    lot of exceptions, mostly bad_alloc()s or system_error()s.

    Given that C++ predates the standard C++ library, it's clearly
    possible to write C++ code without a runtime that throws a lot
    of exceptions. (exceptions didn't show up until C++3.0).

    It is also possible to build a runtime that never throws
    exceptions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to David Brown on Sun Nov 26 14:38:37 2023
    On 11/26/2023 11:27 AM, David Brown wrote:
    On 26/11/2023 18:51, Bonita Montero wrote:
    Am 26.11.2023 um 16:28 schrieb David Brown:

    No, exceptions do not make things easier - they make them /different/.

    Without exceptions you'd have to deal with return codes at each call
    level; this *is* much more work.

    As I said, anyone who claims their preference is always right, is wrong.


    Sometimes it might be helpful to move failure handling to a different
    part of the code, sometimes it makes things harder.

    In C++ exceptions are mostly used for resource collapse situations, i.e.
    out of memory errors, network errors of filesystem failures.

    These are completely different situations.  /Sometimes/ exceptions are a convenient way to handle network or filesystem issues.  They are never a good way of handling out of memory errors, because out of memory errors
    are caused by failures of the code, not external causes.

    Yup. One time I was experimenting with ways to allow a proxy server I
    was coding up to die before exhausting non-paged memory by artificially limiting the memory. This limit was used to make it go into a panic mode
    even though it has exhausted all of the non-paged memory. It just got
    too close. Iiic, it worked fairly well.

    A panic mode would make the server drop all "stale", timedout, ect, connections. It would also try to reclaim as much memory and resources
    as it could, without cancelling currently active and working connections...

    Basically, where you say:
    ________
    They are never a good way of handling out of memory errors
    ________

    Is true.




    With that
    exceptions are the most convenient way to handle that. These types of
    errors are handled in most cases.


    Back here in the real world, most exceptions are /not/ handled -
    programmers implementing the lower level functions think that exceptions
    are someone else's problem and pass the buck up the line.  Programmers implementing the higher level parts don't know that the lower level
    parts might through an exception - they assume functions do what they
    claim to do.

    Used properly, exceptions can be helpful in some situations.
    Unfortunately they are rarely used properly.

    Any code that sees a "bad_alloc" exception is already borked.

    With exceptions you can handle such situations in a clean way with
    a small effort.


    No, you can't.  You cannot magically make more memory appear, nor
    magically fix the crappy code that caused a memory error in the first place.  You are blindly buying into the myth.

    It is not failing because of the exception, it is failing because
    of the bad programming.

    bad_alloc doesn't depend on the programming but rather how much memory
    you have. You might ignore bad_alloc for small allocations since they're
    unlikely to happen, but not for large allocations.

    If you are trying to make an allocation that is too big for the system,
    you are doing things wrong.  Throwing an exception does not help,
    because catching the exception cannot help.

    I don't understand your attitude while programming with C++ because the
    runtime uses exceptions so much.


    The C++ code I use never throws exceptions.  I never have them enabled.
    I don't miss them.

    (I don't use C++ for the kinds of uses where exceptions can be helpful.)





    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Sun Nov 26 15:00:54 2023
    On 11/26/2023 2:38 PM, Chris M. Thomasson wrote:
    On 11/26/2023 11:27 AM, David Brown wrote:
    On 26/11/2023 18:51, Bonita Montero wrote:
    Am 26.11.2023 um 16:28 schrieb David Brown:

    No, exceptions do not make things easier - they make them /different/.

    Without exceptions you'd have to deal with return codes at each call
    level; this *is* much more work.

    As I said, anyone who claims their preference is always right, is wrong.


    Sometimes it might be helpful to move failure handling to a
    different part of the code, sometimes it makes things harder.

    In C++ exceptions are mostly used for resource collapse situations, i.e. >>> out of memory errors, network errors of filesystem failures.

    These are completely different situations.  /Sometimes/ exceptions are
    a convenient way to handle network or filesystem issues.  They are
    never a good way of handling out of memory errors, because out of
    memory errors are caused by failures of the code, not external causes.

    Yup. One time I was experimenting with ways to allow a proxy server I
    was coding up to die before exhausting non-paged memory by artificially limiting the memory. This limit was used to make it go into a panic mode
    even though it has exhausted all of the non-paged memory. It just got
    too close. Iiic, it worked fairly well.

    A panic mode would make the server drop all "stale", timedout, ect, connections. It would also try to reclaim as much memory and resources
    as it could, without cancelling currently active and working connections...

    Basically, where you say:
    ________
    They are never a good way of handling out of memory errors
    ________

    Is true.




    With that
    exceptions are the most convenient way to handle that. These types of
    errors are handled in most cases.


    Back here in the real world, most exceptions are /not/ handled -
    programmers implementing the lower level functions think that
    exceptions are someone else's problem and pass the buck up the line.
    Programmers implementing the higher level parts don't know that the
    lower level parts might through an exception - they assume functions
    do what they claim to do.

    Used properly, exceptions can be helpful in some situations.
    Unfortunately they are rarely used properly.

    Any code that sees a "bad_alloc" exception is already borked.

    With exceptions you can handle such situations in a clean way with
    a small effort.


    No, you can't.  You cannot magically make more memory appear, nor
    magically fix the crappy code that caused a memory error in the first
    place.  You are blindly buying into the myth.

    It is not failing because of the exception, it is failing because
    of the bad programming.

    bad_alloc doesn't depend on the programming but rather how much memory
    you have. You might ignore bad_alloc for small allocations since they're >>> unlikely to happen, but not for large allocations.

    If you are trying to make an allocation that is too big for the
    system, you are doing things wrong.  Throwing an exception does not
    help, because catching the exception cannot help.

    I don't understand your attitude while programming with C++ because the
    runtime uses exceptions so much.


    The C++ code I use never throws exceptions.  I never have them
    enabled. I don't miss them.

    (I don't use C++ for the kinds of uses where exceptions can be helpful.)






    Man, I have not thought about this in a while. Wrt my proxy server, I
    made it express itself where I could reap data on a so-called max and
    min basis. The max would mean it used all non-paged memory and damn near
    blue screened, wrt WinNT. The min means that it got close to gravely
    hurting the system, but things still work...

    Max: we can obtain this by allow the proxy server to crash itself, and
    we are real time taking stats all the way down. We then take a look at
    the data that caused it to crash...

    Min: Divide certain parts of the data obtained from Max by say, two,
    just for starters. So, for instance, we can say this many connections
    crashed it wrt Max. Lets try to limit it to half of those. Then we run
    the proxy server again with that artificial limit that denys new
    connection attempts, and it does not exhaust all of the non-paged memory
    at all. Then we try again by increacing the Min to to Max. Keep in mind
    that if Min=Max, it will use all of the non-paged pool, and bring the
    system to a fucking crawl...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sun Nov 26 15:45:39 2023
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    Huh? This does not make sense to me at all. Have you ever used GQCS or
    GQCSEX at all? Humm...



    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    LOL! You are a character.


    So your hack can break at any time? Keep in mind that I said GQCSEX...
    :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.


    Huh?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 05:24:10 2023
    Am 27.11.2023 um 00:45 schrieb Chris M. Thomasson:
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    Huh? This does not make sense to me at all. Have you ever used GQCS or
    GQCSEX at all? Humm...

    Idiot ...




    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    LOL! You are a character.


    So your hack can break at any time? Keep in mind that I said
    GQCSEX... :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.


    Huh?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 05:23:48 2023
    Am 26.11.2023 um 20:27 schrieb David Brown:

    They are never a good way of handling out of memory errors, because
    out of memory errors are caused by failures of the code, not external causes.

    These errors can happen depending on the type of application and
    bad_alloc()s are a clean way to handle that. All other ways to
    handle that like error codes are less convenient.

    Back here in the real world, most exceptions are /not/ handled - ...

    You're talking about your applications and you think all peopl
    do it like you do.

    No, you can't.  You cannot magically make more memory appear, ...

    No one claims that for exceptions, but you can stop an operation
    and recover from that with RAII and exceptions in a clean way.

    If you are trying to make an allocation that is too big for the system,
    you are doing things wrong. ...

    This usually doesn't depend on the application but on the input
    of a user.

    The C++ code I use never throws exceptions. ...

    Then you must have dropped most parts of the standardy library.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Sun Nov 26 22:22:27 2023
    On 11/26/2023 8:24 PM, Bonita Montero wrote:
    Am 27.11.2023 um 00:45 schrieb Chris M. Thomasson:
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    Huh? This does not make sense to me at all. Have you ever used GQCS or
    GQCSEX at all? Humm...

    Idiot ...

    You are the one that wants to pass a IOCP handle to WFMO, idoit!

    I still don't think you know how GQCS and/or GQCSEX works.







    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    LOL! You are a character.


    So your hack can break at any time? Keep in mind that I said
    GQCSEX... :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.


    Huh?


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 08:57:12 2023
    Am 27.11.2023 um 07:22 schrieb Chris M. Thomasson:
    On 11/26/2023 8:24 PM, Bonita Montero wrote:
    Am 27.11.2023 um 00:45 schrieb Chris M. Thomasson:
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    Huh? This does not make sense to me at all. Have you ever used GQCS
    or GQCSEX at all? Humm...

    Idiot ...

    You are the one that wants to pass a IOCP handle to WFMO, idoit!

    I told you twice that this would make sense if you additionally would
    wait for some other waitable kernel objects. If the IOCP is signalled
    you'd do an additional GQCS() on the IOCP. This actually works with
    Windows 11 and it would be nice if MS would document since which Win-
    dows version it is safe to have a WFMO() on a IOCP.

    I still don't think you know how GQCS and/or GQCSEX works.

    You don't understand what I write.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 09:34:26 2023
    Am 27.11.2023 um 09:28 schrieb Chris M. Thomasson:

    On 11/26/2023 11:57 PM, Bonita Montero wrote:

    I told you twice that this would make sense if you additionally would
    wait for some other waitable kernel objects. If the IOCP is signalled
    you'd do an additional GQCS() on the IOCP. This actually works with
    Windows 11 and it would be nice if MS would document since which Win-
    dows version it is safe to have a WFMO() on a IOCP.

    This is undocumented behavior, and I am not sure what benefit it would
    have anyway. ...

    If you can imagine the benefit of waiting for multiple kernel objects
    you can also imagine the benefit of waiting multiple kernel objects
    where at least one object is an IOCP.

    https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
    ________________
    When bWaitAll is TRUE, the function's wait operation is completed only
    when the states of all objects have been set to signaled. The function
    does not modify the states of the specified objects until the states of
    all objects have been set to signaled. For example, a mutex can be
    signaled, but the thread does not get ownership until the states of the
    other objects are also set to signaled. In the meantime, some other
    thread may get ownership of the mutex, thereby setting its state to nonsignaled.
    ________________

    You can probably just as easily imagine that I don't mean the case
    where we wait for all objects to signal, but only one of them. So
    I don't understand why you're so misunderstanding what I said.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 27 00:28:21 2023
    On 11/26/2023 11:57 PM, Bonita Montero wrote:
    Am 27.11.2023 um 07:22 schrieb Chris M. Thomasson:
    On 11/26/2023 8:24 PM, Bonita Montero wrote:
    Am 27.11.2023 um 00:45 schrieb Chris M. Thomasson:
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"? >>>>>
    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    Huh? This does not make sense to me at all. Have you ever used GQCS
    or GQCSEX at all? Humm...

    Idiot ...

    You are the one that wants to pass a IOCP handle to WFMO, idoit!

    I told you twice that this would make sense if you additionally would
    wait for some other waitable kernel objects. If the IOCP is signalled
    you'd do an additional GQCS() on the IOCP. This actually works with
    Windows 11 and it would be nice if MS would document since which Win-
    dows version it is safe to have a WFMO() on a IOCP.

    This is undocumented behavior, and I am not sure what benefit it would
    have anyway. Iirc, WFMO does not have to be atomic in the way it waits
    on the HANDLE's. It can be broken down into multiple WFSO's.


    I still don't think you know how GQCS and/or GQCSEX works.

    You don't understand what I write.

    Whatever you say. Take note of:

    https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
    ________________
    When bWaitAll is TRUE, the function's wait operation is completed only
    when the states of all objects have been set to signaled. The function
    does not modify the states of the specified objects until the states of
    all objects have been set to signaled. For example, a mutex can be
    signaled, but the thread does not get ownership until the states of the
    other objects are also set to signaled. In the meantime, some other
    thread may get ownership of the mutex, thereby setting its state to nonsignaled.
    ________________

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bonita Montero on Mon Nov 27 10:38:04 2023
    On 27/11/2023 05:23, Bonita Montero wrote:
    Am 26.11.2023 um 20:27 schrieb David Brown:

    They are never a good way of handling out of memory errors, because
    out of memory errors  are caused by failures of the code, not external
    causes.

    These errors can happen depending on the type of application and
    bad_alloc()s are a clean way to handle that. All other ways to
    handle that like error codes are less convenient.

    Out of memory failures are /never/ clean. A program that tries to get
    more memory than is available, using standard allocators, is broken.

    I am not saying that the alternative to bad_alloc exceptions is error
    codes for memory failures - I am saying that the alternative is not
    having memory allocation failures during normal operations.


    Back here in the real world, most exceptions are /not/ handled - ...

    You're talking about your applications and you think all peopl
    do it like you do.

    No, I am talking about reality. /I/ don't use exceptions in C++. They
    are not enabled in my builds. How exceptions are handled is a
    meaningless question for my own code.

    It is extremely difficult to write truly exception-safe code, where the
    code will work correctly in the face of unexpected exceptions happening
    from pretty much any function call. It is possible for some kinds of
    code, when developers have enough knowledge and experience, and are able
    to spend enough time and effort on it. But that is a small minority.

    Oh, and the exception handling is almost always untested - or at least,
    far from comprehensively tested.

    That does not mean that exceptions are always bad - they can be a useful
    way to handle some issues. And it does not mean that error codes or
    other solutions are always good - most error-handling code of any sort
    is untested. It means they are a tool that should be understood and
    used when appropriate, but they should not be over-estimated as you do.

    (If C++ exceptions were as perfect as you imagine, why do you think
    there is such a strong pressure in the C++ standards and development
    groups to move towards things like "zero-overhead deterministic
    exceptions" ? Why do we have std::expected<> in C++23?)


    No, you can't.  You cannot magically make more memory appear, ...

    No one claims that for exceptions, but you can stop an operation
    and recover from that with RAII and exceptions in a clean way.


    Keep telling yourself that. You won't persuade others, but maybe you'll
    feel happier.

    If you are trying to make an allocation that is too big for the
    system, you are doing things wrong. ...

    This usually doesn't depend on the application but on the input
    of a user.


    An application that does not check the input from the user is doing
    things wrong.

    The C++ code I use never throws exceptions. ...

    Then you must have dropped most parts of the standardy library.


    Of course I have. It is useless for the work I do.

    That doesn't mean it is useless for other people and other types of
    coding, of course.

    Actually, most of the standard library uses exceptions only very rarely,
    and in many of these cases the only possible exception is for memory
    allocation failure - which should only occur if your program is badly
    borked.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 10:55:50 2023
    Am 27.11.2023 um 10:38 schrieb David Brown:

    Out of memory failures are /never/ clean.  A program that tries to get
    more memory than is available, using standard allocators, is broken.

    No, usually that depends on the input and this is a valid situation for
    most applications.

    I am not saying that the alternative to bad_alloc exceptions is error
    codes for memory failures - I am saying that the alternative is not
    having memory allocation failures during normal operations.

    That may be true for a lot of applications, but it doesn't hurt to
    handle that and for applications which may require a lot of memory
    - depending on the input - this is absolutely necessary.

    No, I am talking about reality.  /I/ don't use exceptions in C++.
    They are not enabled in my builds.  How exceptions are handled is
    a meaningless question for my own code.

    The standard library would simply terminate if exceptions support is
    disabled because there is no unwinding code for the caller, that's
    un-clean.

    It is extremely difficult to write truly exception-safe code, ...

    Absolutely not if you use RAII consequently.

    An application that does not check the input from the user is doing
    things wrong.

    The application itself can't decide that, it's part of the memory
    management which says if there's enough memory or not, partitially
    depending on the kernel, partitially depending on the user-space
    pooling. The application below that can't do that.

    Of course I have.  It is useless for the work I do.

    And you think it's useless for everyone by saying that handling
    bad_alloc doesn't usually make sense.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bonita Montero on Mon Nov 27 11:19:23 2023
    On 27/11/2023 10:55, Bonita Montero wrote:
    Am 27.11.2023 um 10:38 schrieb David Brown:

    Of course I have.  It is useless for the work I do.

    And you think it's useless for everyone by saying that handling
    bad_alloc doesn't usually make sense.


    No, I said that most of the standard library is useless for the work I
    do, but useful for other people. And I've said that exceptions can be
    useful (but not for my work), but are not the kind of wonderful
    universal "clean" failure handling mechanism you seem to believe.

    The way you move the goalposts around, fail to read posts, and fail to
    address questions, makes it clear that you prefer cultish believe to
    learning and thinking. And since you don't want to learn or think,
    there is little point in discussing with you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Mon Nov 27 12:32:58 2023
    Am 27.11.2023 um 11:19 schrieb David Brown:

    No, I said that most of the standard library is useless for the work
    I do, but useful for other people.  And I've said that exceptions
    can be useful (but not for my work), but are not the kind of wonderful universal "clean" failure handling mechanism you seem to believe.

    That's a matter of opinion. I'm not a painful person like you and I
    think error handling in C++ with exceptions and RAII is great relief
    over manual return code handling and no RAII. With that error handling
    is just catching errors in a central place and reporting that error;
    that's not much work compared to C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 27 14:59:59 2023
    On 11/27/2023 12:34 AM, Bonita Montero wrote:
    Am 27.11.2023 um 09:28 schrieb Chris M. Thomasson:

    On 11/26/2023 11:57 PM, Bonita Montero wrote:

    I told you twice that this would make sense if you additionally would
    wait for some other waitable kernel objects. If the IOCP is signalled
    you'd do an additional GQCS() on the IOCP. This actually works with
    Windows 11 and it would be nice if MS would document since which Win-
    dows version it is safe to have a WFMO() on a IOCP.

    This is undocumented behavior, and I am not sure what benefit it would
    have anyway. ...

    If you can imagine the benefit of waiting for multiple kernel objects
    you can also imagine the benefit of waiting multiple kernel objects
    where at least one object is an IOCP.
    [...]

    No need for it at all. Well, at least is none of my server work.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to David Brown on Mon Nov 27 15:15:08 2023
    On 11/26/2023 4:54 AM, David Brown wrote:
    On 26/11/2023 00:29, Michael S wrote:
    On Sat, 25 Nov 2023 19:30:03 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-11-25, Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 24.11.2023 um 19:33 schrieb Kaz Kylheku:
    It's just a syntactic sugar to avoid forgetting to unlock ...

    RAII is magnitudes more convenient and lass error-prone
    than manual resource handling.

    Sure, but that's not necessarily an effective way to "sell" it to
    someone who is being skeptical.

    I would argue that is is not 'less error-prone' than
    explicit locking, properly written and unit tested.

    RAII is horrible, but unfortunately necessary in big undisciplined C++
    projects.
    The root bug in C++ language design are constructors that can fail, but
    can't return error code.

    That's easily solved in cases like this - write constructors that can't
    fail.

    In the kind of systems I work with, something like acquiring a mutex
    cannot fail unless there is a major system failure that renders it
    unsafe to run anything - the system then turns off all the machinery it controls, logs everything it can, and alerts the user to the failure.
    That applies equally to a free-standing "lock" function or an RAII constructor solution.  There is no need for exceptions, error codes, or anything of that sort.

    That problem begot exceptions. And the presence of exceptions begot
    RAII. RAII is bad, but less bad than other solutions.


    I am not a fan of exceptions in low-level code or control code.  They
    are fine in high-level PC stuff when dealing with things that can
    reasonably fail but where you don't want to bother with all the details
    all the time - such as a constructor for opening a handle to a database.
     (I mostly use Python for that kind of thing anyway, but that's just personal choice.)



    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier,

    Amen. :^)


    and RAII is just another useful tool.
    (And you can compile with exceptions fully disabled.)


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Bonita Montero on Mon Nov 27 15:19:01 2023
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    So your hack can break at any time? Keep in mind that I said GQCSEX...
    :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.


    God knows what WFMO is going to do to that IOCP handle. It could break
    things at a lower level of logic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Scott Lurndal on Mon Nov 27 15:24:32 2023
    On 11/16/2023 9:45 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 16.11.2023 um 16:02 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 15.11.2023 um 21:08 schrieb Chris M. Thomasson:

    Are you sure its 100% atomic for use in multi-thread sync algorithms? >>>>
    If MSVC generates that code it's atomic.

    No, if Intel architecture documentation specifies that it is
    single-copy atomic, then it is single-copy atomic.

    MSVC relies for sure on the right behaviour.

    Right. Keep thinking that.


    MSVC is just a poor-to-middling C compiler.

    MSVC has the strongest C++20-conformance but the weakest optimizer.

    Faint praise, indeed. Nobody other than Herb cares about C++20 conformance.

    ;^D Sutters mill...



    Most real world C++ code relies on C++11 for portability. Most
    of our systems and our customers systems don't even support C++17.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Tue Nov 28 09:03:58 2023
    On 28/11/2023 00:15, Chris M. Thomasson wrote:
    On 11/26/2023 4:54 AM, David Brown wrote:


    Once you stop thinking that code can fail, and write code that doesn't
    fail, it all becomes much easier,

    Amen. :^)

    It would probably have been better to say "Once you stop thinking that
    code always has the possibility of failure, you can start writing code
    that cannot fail." Code can still fail - you might have made a mistake
    in the code. But then it is /your/ fault, as the programmer - take responsibility for that, don't punt it down the line as an exception to
    the innocent programmer that calls your function.

    If it is inevitable that your function might not be able to do its main
    job - it's trying to read a file that might not exist, or connect to a
    database that might not be online - make such failures explicitly part
    of the specification of the function.

    Maybe you think exceptions are the best way to communicate such
    failures. Then your specification is "This function reads the file and
    returns its contents, or throws a FileNotAccessible exception". The
    real trouble with the whole concept of exceptions is that people think
    they mean you can specify the function as "This function reads the file
    and returns its contents", and that somehow exceptions magically fix any failures, or that code further up the food chain can compensate for the
    problem - and no one takes responsibility. Error code return can be
    ugly, and std::expected<> returns can mean changing lots of code along
    the way, but these make the failures /explicit/ - you have to keep
    looking at them until you deal with them. They are part of the
    specification of the code, not an afterthought that magically floats
    around until it gets swept under the carpet.



    and RAII is just another useful tool. (And you can compile with
    exceptions fully disabled.)



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Chris M. Thomasson on Tue Nov 28 15:13:30 2023
    On 11/27/2023 3:19 PM, Chris M. Thomasson wrote:
    On 11/26/2023 2:35 AM, Bonita Montero wrote:
    Am 26.11.2023 um 00:06 schrieb Chris M. Thomasson:

    Huh? Drill down on that. What do you mean wrt "_after_ you waited"?

    If you want to wait mor than on the IOCP itself you could use WFMO
    and if the IOCP was signalled GQCS afterwards. With an IOCP you can
    wait only for the IOCP and maybe an alert if you use GQCSE.

    So your hack can break at any time? ...

    It would be nice if MS would document since when this works.

    So your hack can break at any time? Keep in mind that I said
    GQCSEX... :^) Its more efficient that GQCS. ...

    It's not about the efficiency but that you could be signalled other
    waitable kernel objects also.


    God knows what WFMO is going to do to that IOCP handle. It could break
    things at a lower level of logic.

    Nt 4.0 source code is out there. Humm... So, you should dig into it and
    see what happens to IOCP if you pass its handle to WFMO...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris M. Thomasson@21:1/5 to Pavel on Tue Nov 28 15:32:52 2023
    On 11/14/2023 5:42 PM, Pavel wrote:
    Kaz Kylheku wrote:
    On 2023-11-14, Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
    wrote:
    "as-if" and "according to the scheduling policy (if applicable)" here
    are important. The standard intent may be to permit only the threads
    that are "unblocked" contend for the mutex -- as opposed to all
    non-blocked threads.

    It's possible that only one thread is unblocked by a
    pthread_cond_signal. It takes at least two parties to contend for
    something.

    My point was that the "unblocked" thread could be the thread that "won"
    the contention. "as-if" there meant that the threads didn't really need
    to be all woken up / unblocked and start contending; but the contention
    could be resolved according to the scheduling policy (the same one that
    would be applied if they were actually running and contending for the
    mutex, without any waiting for the condvar) within the signal call.

    The flexibility of the API allows us to signal inside and/or outside of
    a locked mutex region. So, whats the problem?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)