• Re: a try_lock pattern...

    From [email protected]@21:1/5 to All on Sun Sep 29 09:01:50 2024
    On Sun, 29 Sep 2024 08:15:23 +0200
    Bonita Montero <[email protected]> boringly babbled:
    try_lock in that way on GitHub; you won't find code like this. No
    one uses try_lock.

    Translation: "I don't use try_lock"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to All on Sun Sep 29 14:56:27 2024
    On Sun, 29 Sep 2024 12:54:38 +0200
    Bonita Montero <[email protected]> boringly babbled:
    Am 29.09.2024 um 11:01 schrieb [email protected]:
    On Sun, 29 Sep 2024 08:15:23 +0200
    Bonita Montero <[email protected]> boringly babbled:
    try_lock in that way on GitHub; you won't find code like this. No
    one uses try_lock.

    Translation: "I don't use try_lock"

    Tell me under which circumstances the mentioned two constraints
    don't matter.

    Don't know, don't care. But I can imagine a number of scenarios where
    someone might use try_lock when they don't want the thread to hang but go
    do something more useful instead if it can't obtain a lock.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Chris M. Thomasson on Sun Sep 29 18:03:26 2024
    "Chris M. Thomasson" <[email protected]> writes:
    On 9/28/2024 12:53 AM, Paavo Helde wrote:

    Nice! Thanks for taking the time to give it a go Paavo. Basically, the
    main "pattern" is in the ct_shared::work_a_lock() function: >_____________________
    void work_a_lock()
    {
    while (! m_lock_a.try_lock())
    {
    if (! work_b_try(1))
    {
    m_lock_a.lock();
    break;
    }
    }
    }
    _____________________


    Seems a waste. Just fire up two threads one for b work
    and one for a work. Let the OS manage the CPUs.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paavo Helde@21:1/5 to Chris M. Thomasson on Sat Sep 28 10:53:50 2024
    On 28.09.2024 10:20, Chris M. Thomasson wrote:
    This is just a quick test of my try_lock pattern thing. ct_shared::m_b
    counts how many times extra work was able to be done instead of spinning
    on the ct_shared::m_lock_a mutex. Take careful notice of the ct_shared::work_a_lock() function.

    Can you test it out? A large number for ct_shared::m_b is ideal. It
    means extra work was being done. Avoids spinning in an interesting way.

    my crude code, can you compile and run it okay? Thanks: _________________________
    #include <cassert>
    #include <iostream>
    #include <atomic>
    #include <mutex>
    #include <functional>
    #include <thread>


    #define CT_THREADS 10
    #define CT_ITERS 1000000


    // Test Threads...
    struct ct_shared
    {
        long m_a;
        unsigned long m_b;
        std::mutex m_lock_a;
        std::mutex m_lock_b;

        ct_shared() : m_a(0), m_b(0) {}


        bool work_b_try(unsigned long addend)
        {
            if (! m_lock_b.try_lock())
            {
                return false;
            }

            {
                m_b += addend;
            }

            m_lock_b.unlock();

            return true;
        }

        // The main pattern...
        void work_a_lock()
        {
            while (! m_lock_a.try_lock())
            {
                if (! work_b_try(1))
                {
                    m_lock_a.lock();
                    break;
                }
            }
        }

        void work_a_unlock()
        {
            m_lock_a.unlock();
        }

        void work_a(long addend)
        {
            work_a_lock();

            {
                m_a += addend;
            }

            work_a_unlock();
        }
    };


    // Test out a pattern...
    void
    ct_thread(
        ct_shared& shared
    ) {
        for (unsigned long i = 0; i < CT_ITERS; ++i)
        {
            shared.work_a(1);
            std::this_thread::yield();
            shared.work_a(-1);
        }
    }


    // Get things going...
    int
    main()
    {
        std::cout << "ct try_lock pattern Test...\n\n";

        {
            ct_shared shared;

            std::thread threads[CT_THREADS];

            // Create threads...
            std::cout << "launching threads..." << std::endl;

            for (unsigned long i = 0; i < CT_THREADS; ++i)
            {
                threads[i] = std::thread(ct_thread, std::ref(shared));

            }

            std::cout << "processing...\n" << std::endl;

            // Join threads...
            for (unsigned long i = 0; i < CT_THREADS; ++i)
            {
                threads[i].join();
            }

            // Sanity check...
            {
                std::cout << "shared.m_a = " << shared.m_a << "\n";
                std::cout << "shared.m_b = " << shared.m_b << "\n";

                if (shared.m_a != 0)
                {
                    std::cout << "\n\nAAHHHH SHHHIIITT!!\n";
                }

            }
        }

        std::cout << "\nct try_lock pattern Test completed.\n\n";

        return 0;
    }
    _________________________


    What results do you get for ct_shared::m_b? Here is a result I get: _____________________________
    ct try_lock pattern Test...

    launching threads...
    processing...

    shared.m_a = 0
    shared.m_b = 8664093

    ct try_lock pattern Test completed.
    _____________________________

    Well, shared.m_b is a high number... That is a lot potential kernel
    calls that were skipped in favor of real work.

    Any thoughts? Thanks.

    Seems to compile and work fine with VS2022 on Windows x86_64

    ct try_lock pattern Test...

    launching threads...
    processing...

    shared.m_a = 0
    shared.m_b = 6976047

    ct try_lock pattern Test completed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)