• base code for a proxy experiment...

    From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Tue Oct 29 22:38:16 2024
    From Newsgroup: comp.lang.c++

    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage. _____________________________________________________

    #include <iostream>
    #include <functional>
    #include <thread>
    #include <atomic>
    #include <mutex>
    #include <vector>


    #define CT_THREAD_WORKERS_N 4
    #define CT_THREAD_POLL_N 1
    #define CT_THREAD_N (CT_THREAD_WORKERS_N + CT_THREAD_POLL_N)
    #define CT_PER_THREAD_LOCKS_N 2



    thread_local struct ct_per_thread* ct_g_per_thread = nullptr;




    struct ct_shared
    {
    std::atomic<unsigned long> m_version;
    std::mutex m_cout_mutex;
    std::mutex m_threads_mutex;
    std::vector<struct ct_per_thread*> m_threads;

    ct_shared()
    : m_version(0)
    {
    m_cout_mutex.lock();
    std::cout << "ct_shared::ct_shared()" << std::endl;
    m_cout_mutex.unlock();
    }

    ~ct_shared()
    {
    m_cout_mutex.lock();
    std::cout << "ct_shared::~ct_shared()" << std::endl;
    m_cout_mutex.unlock();
    }
    };




    struct ct_per_thread
    {
    ct_shared& m_shared;
    unsigned long m_id;
    std::mutex m_locks[CT_PER_THREAD_LOCKS_N];

    ct_per_thread(
    ct_shared& shared,
    unsigned long id
    ): m_shared(shared),
    m_id(id)
    {
    m_shared.m_cout_mutex.lock();
    std::cout << "(" << m_id <<
    ")->ct_per_thread::ct_per_thread()" << std::endl;
    m_shared.m_cout_mutex.unlock();
    }

    ~ct_per_thread()
    {
    m_shared.m_cout_mutex.lock();
    std::cout << "(" << m_id <<
    ")->ct_per_thread::~ct_per_thread()" << std::endl;
    m_shared.m_cout_mutex.unlock();

    // a big humm...
    ct_g_per_thread = nullptr; // humm, need to ponder...
    }
    };




    struct ct_poll_thread
    {
    ct_shared& m_shared;
    unsigned long m_id;

    ct_poll_thread(
    ct_shared& shared,
    unsigned long id
    ) : m_shared(shared),
    m_id(id)
    {
    m_shared.m_cout_mutex.lock();
    std::cout << "(" << m_id <<
    ")->ct_poll_thread::ct_poll_thread()" << std::endl;
    m_shared.m_cout_mutex.unlock();
    }

    ~ct_poll_thread()
    {
    m_shared.m_cout_mutex.lock();
    std::cout << "(" << m_id << ")->ct_poll_thread::~ct_poll_thread()" << std::endl;
    m_shared.m_cout_mutex.unlock();

    ct_g_per_thread = nullptr; // humm, need to ponder...
    }
    };





    void
    ct_worker_thread(
    ct_shared& shared,
    unsigned long id
    ) {
    // get into the groove... ;^D lol.
    {
    shared.m_cout_mutex.lock();
    std::cout << "ct_worker_thread(" << id << "), hello there!"
    << std::endl;
    shared.m_cout_mutex.unlock();
    }

    // Humm...
    // It should work fine in modern C++! :^)
    {
    thread_local ct_per_thread l_ct_per_thread(shared, id);
    ct_g_per_thread = &l_ct_per_thread;
    }

    // Okay, got our per thread access via ct_g_per_thread! :^)
    {

    }

    //Well, we have our per thread access. Use it!
    {
    ct_g_per_thread->m_shared.m_cout_mutex.lock();
    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;
    ct_g_per_thread->m_shared.m_cout_mutex.unlock();
    }
    }



    void
    ct_poll_thread(
    ct_shared& shared,
    unsigned long id
    ) {
    // get into the groove... ;^D lol.
    {
    shared.m_cout_mutex.lock();
    std::cout << "\nct_poll_thread(" << id << "), hello there!"
    << std::endl;
    shared.m_cout_mutex.unlock();
    }

    // Humm...
    // It should work fine in modern C++! :^)
    {
    thread_local ct_per_thread l_ct_per_thread(shared, id);
    ct_g_per_thread = &l_ct_per_thread;
    }

    // Okay, got our per thread access via ct_g_per_thread! :^)
    {
    for (unsigned long i = 0; i < 42; ++i)
    {
    shared.m_cout_mutex.lock();
    std::cout << "\nct_poll_thread(" << id << "), iterate:"
    << i << std::endl;
    shared.m_cout_mutex.unlock();
    }
    }

    //Well, we have our per thread access. Use it!
    {
    ct_g_per_thread->m_shared.m_cout_mutex.lock();
    std::cout << "ct_poll_thread(" << id << "), goodbye
    everybody from here! ;^o\n" << std::endl;
    ct_g_per_thread->m_shared.m_cout_mutex.unlock();
    }
    }



    int
    main()
    {
    std::cout << "ct_proxy_collector testing 123...\n";
    std::cout << "_________________________________\n\n" << std::endl;

    {
    ct_shared shared;

    {
    std::thread threads[CT_THREAD_N];

    std::cout << "Launching threads and processing...\n";
    std::cout << "____________________\n" << std::endl;

    {
    {
    // Create worker threads...
    for (unsigned long i = 0; i < CT_THREAD_WORKERS_N; ++i)
    {
    threads[i] = std::thread(ct_worker_thread, std::ref(shared), i);
    }

    // Create poll threads...
    for (unsigned long i = CT_THREAD_WORKERS_N; i < CT_THREAD_N; ++i)
    {
    threads[i] = std::thread(ct_poll_thread, std::ref(shared), i);
    }
    }

    // Wait for them...
    for (unsigned long i = 0; i < CT_THREAD_N; ++i)
    {
    threads[i].join();
    }
    }

    std::cout << "____________________\n" << std::endl;
    }

    std::cout << "Complete!\n";
    std::cout << "____________________\n" << std::endl;
    }

    std::cout << "\nFin!\n____________________\n" << std::endl;

    return 0;
    }


    _____________________________________________________
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Wed Oct 30 18:09:39 2024
    From Newsgroup: comp.lang.c++

    On 30.10.2024 07:38, Chris M. Thomasson wrote:
    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage. _____________________________________________________

    #include <iostream>
    #include <functional>
    #include <thread>
    #include <atomic>
    #include <mutex>
    #include <vector>


    #define CT_THREAD_WORKERS_N 4
    #define CT_THREAD_POLL_N 1
    #define CT_THREAD_N (CT_THREAD_WORKERS_N + CT_THREAD_POLL_N)
    #define CT_PER_THREAD_LOCKS_N 2



    thread_local struct ct_per_thread* ct_g_per_thread = nullptr;




    struct ct_shared
    {
        std::atomic<unsigned long> m_version;
        std::mutex m_cout_mutex;
        std::mutex m_threads_mutex;
        std::vector<struct ct_per_thread*> m_threads;

        ct_shared()
        :   m_version(0)
        {
            m_cout_mutex.lock();
                std::cout << "ct_shared::ct_shared()" << std::endl;
            m_cout_mutex.unlock();
        }

        ~ct_shared()
        {
            m_cout_mutex.lock();
                std::cout << "ct_shared::~ct_shared()" << std::endl;
            m_cout_mutex.unlock();
        }
    };




    struct ct_per_thread
    {
        ct_shared& m_shared;
        unsigned long m_id;
        std::mutex m_locks[CT_PER_THREAD_LOCKS_N];

        ct_per_thread(
            ct_shared& shared,
            unsigned long id
        ):  m_shared(shared),
            m_id(id)
        {
            m_shared.m_cout_mutex.lock();
                std::cout << "(" << m_id << ")-
    ct_per_thread::ct_per_thread()" << std::endl;
            m_shared.m_cout_mutex.unlock();
        }

        ~ct_per_thread()
        {
            m_shared.m_cout_mutex.lock();
                std::cout << "(" << m_id << ")-
    ct_per_thread::~ct_per_thread()" << std::endl;
            m_shared.m_cout_mutex.unlock();

            // a big humm...
            ct_g_per_thread = nullptr; // humm, need to ponder...
        }
    };




    struct ct_poll_thread
    {
        ct_shared& m_shared;
        unsigned long m_id;

        ct_poll_thread(
            ct_shared& shared,
            unsigned long id
        ) : m_shared(shared),
            m_id(id)
        {
            m_shared.m_cout_mutex.lock();
                std::cout << "(" << m_id << ")-
    ct_poll_thread::ct_poll_thread()" << std::endl;
            m_shared.m_cout_mutex.unlock();
        }

        ~ct_poll_thread()
        {
            m_shared.m_cout_mutex.lock();
                std::cout << "(" << m_id << ")-
    ct_poll_thread::~ct_poll_thread()" << std::endl;
            m_shared.m_cout_mutex.unlock();

            ct_g_per_thread = nullptr; // humm, need to ponder...
        }
    };





    void
    ct_worker_thread(
        ct_shared& shared,
        unsigned long id
    ) {
        // get into the groove... ;^D lol.
        {
            shared.m_cout_mutex.lock();
                std::cout << "ct_worker_thread(" << id << "), hello there!"
    << std::endl;
            shared.m_cout_mutex.unlock();
        }

        // Humm...
        // It should work fine in modern C++! :^)
        {
            thread_local ct_per_thread l_ct_per_thread(shared, id);
            ct_g_per_thread = &l_ct_per_thread;
        }

        // Okay, got our per thread access via ct_g_per_thread! :^)
        {

        }

        //Well, we have our per thread access. Use it!
        {
            ct_g_per_thread->m_shared.m_cout_mutex.lock();
                std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;
            ct_g_per_thread->m_shared.m_cout_mutex.unlock();
        }
    }



    void
    ct_poll_thread(
        ct_shared& shared,
        unsigned long id
    ) {
        // get into the groove... ;^D lol.
        {
            shared.m_cout_mutex.lock();
                std::cout << "\nct_poll_thread(" << id << "), hello there!"
    << std::endl;
            shared.m_cout_mutex.unlock();
        }

        // Humm...
        // It should work fine in modern C++! :^)
        {
            thread_local ct_per_thread l_ct_per_thread(shared, id);
            ct_g_per_thread = &l_ct_per_thread;
        }

        // Okay, got our per thread access via ct_g_per_thread! :^)
        {
            for (unsigned long i = 0; i < 42; ++i)
            {
                shared.m_cout_mutex.lock();
                    std::cout << "\nct_poll_thread(" << id << "), iterate:"
    << i << std::endl;
                shared.m_cout_mutex.unlock();
            }
        }

        //Well, we have our per thread access. Use it!
        {
            ct_g_per_thread->m_shared.m_cout_mutex.lock();
                std::cout << "ct_poll_thread(" << id << "), goodbye everybody from here! ;^o\n" << std::endl;
            ct_g_per_thread->m_shared.m_cout_mutex.unlock();
        }
    }



    int
    main()
    {
        std::cout << "ct_proxy_collector testing 123...\n";
        std::cout << "_________________________________\n\n" << std::endl;

        {
            ct_shared shared;

            {
                std::thread threads[CT_THREAD_N];

                std::cout << "Launching threads and processing...\n";
                std::cout << "____________________\n" << std::endl;

                {
                    {
                        // Create worker threads...
                        for (unsigned long i = 0; i < CT_THREAD_WORKERS_N;
    ++i)
                        {
                            threads[i] = std::thread(ct_worker_thread,
    std::ref(shared), i);
                        }

                        // Create poll threads...
                        for (unsigned long i = CT_THREAD_WORKERS_N; i <
    CT_THREAD_N; ++i)
                        {
                            threads[i] = std::thread(ct_poll_thread,
    std::ref(shared), i);
                        }
                    }

                    // Wait for them...
                    for (unsigned long i = 0; i < CT_THREAD_N; ++i)
                    {
                        threads[i].join();
                    }
                }

                std::cout << "____________________\n" << std::endl;
            }

            std::cout << "Complete!\n";
            std::cout << "____________________\n" << std::endl;
        }

        std::cout << "\nFin!\n____________________\n" << std::endl;

        return 0;
    }


    _____________________________________________________


    Seems to compile and run fine with VS2022 on x64:

    ct_proxy_collector testing 123...
    _________________________________


    ct_shared::ct_shared()
    Launching threads and processing...
    ____________________

    ct_worker_thread(0), hello there!
    (0)->ct_per_thread::ct_per_thread()
    ct_worker_thread(0), goodbye everybody from here! ;^o
    ct_worker_thread(1), hello there!
    (1)->ct_per_thread::ct_per_thread()
    ct_worker_thread(1), goodbye everybody from here! ;^o
    ct_worker_thread(3), hello there!
    (3)->ct_per_thread::ct_per_thread()
    ct_worker_thread(3), goodbye everybody from here! ;^o
    ct_worker_thread(2), hello there!
    (2)->ct_per_thread::ct_per_thread()
    ct_worker_thread(2), goodbye everybody from here! ;^o (0)->ct_per_thread::~ct_per_thread()

    ct_poll_thread(4), hello there!
    (4)->ct_per_thread::ct_per_thread()
    (1)->ct_per_thread::~ct_per_thread()

    ct_poll_thread(4), iterate:0

    ct_poll_thread(4), iterate:1

    ct_poll_thread(4), iterate:2

    ct_poll_thread(4), iterate:3

    ct_poll_thread(4), iterate:4

    ct_poll_thread(4), iterate:5

    ct_poll_thread(4), iterate:6

    ct_poll_thread(4), iterate:7

    ct_poll_thread(4), iterate:8

    ct_poll_thread(4), iterate:9

    ct_poll_thread(4), iterate:10

    ct_poll_thread(4), iterate:11

    ct_poll_thread(4), iterate:12

    ct_poll_thread(4), iterate:13

    ct_poll_thread(4), iterate:14

    ct_poll_thread(4), iterate:15

    ct_poll_thread(4), iterate:16

    ct_poll_thread(4), iterate:17

    ct_poll_thread(4), iterate:18

    ct_poll_thread(4), iterate:19

    ct_poll_thread(4), iterate:20

    ct_poll_thread(4), iterate:21

    ct_poll_thread(4), iterate:22

    ct_poll_thread(4), iterate:23

    ct_poll_thread(4), iterate:24

    ct_poll_thread(4), iterate:25

    ct_poll_thread(4), iterate:26

    ct_poll_thread(4), iterate:27

    ct_poll_thread(4), iterate:28

    ct_poll_thread(4), iterate:29

    ct_poll_thread(4), iterate:30

    ct_poll_thread(4), iterate:31

    ct_poll_thread(4), iterate:32

    ct_poll_thread(4), iterate:33

    ct_poll_thread(4), iterate:34

    ct_poll_thread(4), iterate:35

    ct_poll_thread(4), iterate:36

    ct_poll_thread(4), iterate:37

    ct_poll_thread(4), iterate:38

    ct_poll_thread(4), iterate:39

    ct_poll_thread(4), iterate:40

    ct_poll_thread(4), iterate:41
    ct_poll_thread(4), goodbye everybody from here! ;^o

    (3)->ct_per_thread::~ct_per_thread()
    (2)->ct_per_thread::~ct_per_thread()
    (4)->ct_per_thread::~ct_per_thread()
    ____________________

    Complete!
    ____________________

    ct_shared::~ct_shared()

    Fin!
    ____________________



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Oct 30 12:30:34 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 9:09 AM, Paavo Helde wrote:
    On 30.10.2024 07:38, Chris M. Thomasson wrote:
    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage.
    _____________________________________________________
    [...]
    _____________________________________________________


    Seems to compile and run fine with VS2022 on x64:

    Thank you, Paavo. :^) It sure seems like the per-thread dtors are
    working fine. Nice! It's been a while since I have tested them like
    this. Iirc, around 10 years ago I was having some trouble using them.
    Some dtors were not being called, damn it. The compilers got better for
    sure. It should also work fine with GCC (not tested yet), or any other
    C++11 compiler. This is a very important aspect. The dtors are being
    called deeper in the logic, so to speak.

    Fwiw, I created this as a little sort of, "template" if you will, for my
    proxy work I am going to do for a little fun. It's a change from
    hardcore fractal work all the time. That much math can start to take a
    toll... lol. ;^)

    I think this test code should be perfectly fine standard C++... Unless,
    I am missing something here. Humm... I don't think so.

    Now I need to allow the polling thread to have access to the worker
    threads per-thread structures.

    This base will help me be able to test all sort of interesting proxy algorithms.





    ct_proxy_collector testing 123...
    _________________________________
    [...]
    Complete!
    ____________________

    ct_shared::~ct_shared()

    Fin!
    ____________________

    [...]
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Oct 30 12:46:26 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 12:30 PM, Chris M. Thomasson wrote:
    On 10/30/2024 9:09 AM, Paavo Helde wrote:
    On 30.10.2024 07:38, Chris M. Thomasson wrote:
    [...]
    This base will help me be able to test all sort of interesting proxy algorithms.

    I also need to create some tests in Relacy. However, I cannot remember
    right now if it can model an async membar... So, wrt my async tests, I
    will be using FlushProcessWriteBuffers, windows, but shit happens.
    Humm... It would be nice if C/C++ made a standard async membar
    available. No need to use #define WIN32_LEAN_AND_MEAN and #include
    <windows.h> ;^)


    [...]

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Wed Oct 30 16:25:38 2024
    From Newsgroup: comp.lang.c++

    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
    { a.lock() } -> std::same_as<void>;
    { a.unlock() } -> std::same_as<void>;
    };

    /**
    * @tparm T proxy collector type
    * @tparm B base object type for managed objects
    */
    template<typename T, typename B>
    concept ProxyType = requires(T proxy, B * obj)
    {
    BasicLockable<T>;
    std::has_virtual_destructor<B>::value; // base object
    must have virtual destructor

    { proxy.unregister() } -> std::same_as<void>; // unregister
    thread

    { proxy.retire(obj) } -> std::same_as<bool>; // true if
    reclaim thread needs notification
    { proxy.retire_is_lockfree() } -> std::same_as<bool>;
    { proxy.retire_is_synchronous() } -> std::same_as<bool>;

    { proxy.try_reclaim() } -> std::same_as<bool>; // false if no
    more pending retires

    };

    I did smrproxy and shared lock. I didn't do arcproxy or
    any others.

    try_reclaim is so you can run your own reclaim thread with
    retire and try_reclaim results to you could do notifies if
    necesary.
    retire_is_syncrhonous means no deferred retire so no
    try_reclaim is necessary. Kind of moot. If I go
    with rust I'll use rust traits.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Oct 30 13:58:13 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 1:25 PM, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
        { a.lock() } -> std::same_as<void>;
        { a.unlock() } -> std::same_as<void>;
    };

    /**
     * @tparm T proxy collector type
     * @tparm B base object type for managed objects
     */
    template<typename T, typename B>
    concept ProxyType = requires(T proxy, B * obj)
    {
        BasicLockable<T>;
        std::has_virtual_destructor<B>::value;              // base object
    must have virtual destructor

        { proxy.unregister() } -> std::same_as<void>;       // unregister
    thread

        { proxy.retire(obj) } -> std::same_as<bool>;        // true if
    reclaim thread needs notification
        { proxy.retire_is_lockfree() } -> std::same_as<bool>;
        { proxy.retire_is_synchronous() } -> std::same_as<bool>;

        { proxy.try_reclaim() } -> std::same_as<bool>;      // false if no
    more pending retires

    };

    I did smrproxy and shared lock.  I didn't do arcproxy or
    any others.

    try_reclaim is  so you can run your own reclaim thread with
    retire and try_reclaim results to you could do notifies if
    necesary.
    retire_is_syncrhonous means no deferred retire so no
    try_reclaim is necessary.  Kind of moot.  If I go
    with rust I'll use rust traits.

    Seems okay to me. Actually, it kind of fits into the API I did for the following proxy test:

    https://pastebin.com/raw/nPVYXbWM
    _____________
    public:
    collector& acquire()
    {
    [...]
    }

    void release(collector& c)
    {
    [...]
    }


    collector& sync(collector& c)
    {
    [...]
    }


    void collect()
    {
    prv_quiesce_begin();
    }


    void collect(collector& c, ct_node* n)
    {
    [...]
    }
    };
    _____________


    Now wrt you retire, it's akin to my collect function... wait-free single producer single consumer queues to allow the poll thread to pick them
    up? A worker thread retires a node (producer), the _single_ polling
    thread consumes it?

    Actually, I can get away with doing something interesting in my collect(collector& c, ct_node* n) function:
    ______________
    void collect(collector& c, ct_node* n)
    {
    if (! n) return;

    // link node into the defer list.
    ct_node* prev = c.m_defer.exchange(n, std::memory_order_relaxed);
    n->m_defer_next = prev;
    //^^^^^^^^^^^^^^^^^^^^^^^^
    // RIGHT ABOVE




    // bump the defer count and begin quiescence process if over
    // the limit.
    std::uint32_t count =
    c.m_defer_count.fetch_add(1, std::memory_order_relaxed) + 1;

    if (count >= (T_defer_limit / 2))
    {
    prv_quiesce_begin();
    }
    }
    ______________


    A fun way to insert into a lock-free lifo. ;^)

    The defer limit is just there to try to trigger quiescent states before
    too many nodes pile up... :^)


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Oct 30 14:06:25 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 1:25 PM, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
        { a.lock() } -> std::same_as<void>;
        { a.unlock() } -> std::same_as<void>;

    Humm... For some reason it might be helpful for lock to return a epoch,
    or proxy_state thing or some sort of state. Then pass it into unlock.

    proxy_state lock();
    void unlock(proxy_state const&);

    Fwiw, I do this wrt my acquire and release functions, akin to your lock
    and unlock functions:

    collector& acquire()
    {
    [...]
    }

    void release(collector& c)
    {
    [...]
    }

    Humm...



    };

    /**
     * @tparm T proxy collector type
     * @tparm B base object type for managed objects
     */
    template<typename T, typename B>
    concept ProxyType = requires(T proxy, B * obj)
    {
        BasicLockable<T>;
        std::has_virtual_destructor<B>::value;              // base object
    must have virtual destructor

        { proxy.unregister() } -> std::same_as<void>;       // unregister
    thread

        { proxy.retire(obj) } -> std::same_as<bool>;        // true if
    reclaim thread needs notification
        { proxy.retire_is_lockfree() } -> std::same_as<bool>;
        { proxy.retire_is_synchronous() } -> std::same_as<bool>;

        { proxy.try_reclaim() } -> std::same_as<bool>;      // false if no
    more pending retires

    };

    I did smrproxy and shared lock.  I didn't do arcproxy or
    any others.

    try_reclaim is  so you can run your own reclaim thread with
    retire and try_reclaim results to you could do notifies if
    necesary.
    retire_is_syncrhonous means no deferred retire so no
    try_reclaim is necessary.  Kind of moot.  If I go
    with rust I'll use rust traits.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Louis Krupp@lkrupp@invalid.pssw.com.invalid to comp.lang.c++ on Wed Oct 30 16:01:24 2024
    From Newsgroup: comp.lang.c++

    On 10/29/2024 11:38 PM, Chris M. Thomasson wrote:
    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage. _____________________________________________________

    <snip>
    _____________________________________________________

    Compiled clean with -Wall -Werror using:

        g++ (GCC) 14.2.1 20240912 (Red Hat 14.2.1-3).

    Output is:

    =====================================================
    ct_proxy_collector testing 123...
    _________________________________


    ct_shared::ct_shared()
    Launching threads and processing...
    ____________________

    ct_worker_thread(0), hello there!
    ct_worker_thread(2), hello there!
    (2)->ct_per_thread::ct_per_thread()
    ct_worker_thread(3), hello there!
    (3)->ct_per_thread::ct_per_thread()
    (0)->ct_per_thread::ct_per_thread()
    ct_worker_thread(2), goodbye everybody from here! ;^o (2)->ct_per_thread::~ct_per_thread()
    ct_worker_thread(3), goodbye everybody from here! ;^o (3)->ct_per_thread::~ct_per_thread()
    ct_worker_thread(0), goodbye everybody from here! ;^o (0)->ct_per_thread::~ct_per_thread()
    ct_worker_thread(1), hello there!
    (1)->ct_per_thread::ct_per_thread()
    ct_worker_thread(1), goodbye everybody from here! ;^o (1)->ct_per_thread::~ct_per_thread()

    ct_poll_thread(4), hello there!
    (4)->ct_per_thread::ct_per_thread()

    ct_poll_thread(4), iterate:0

    ct_poll_thread(4), iterate:1

    ct_poll_thread(4), iterate:2

    ct_poll_thread(4), iterate:3

    ct_poll_thread(4), iterate:4

    ct_poll_thread(4), iterate:5

    ct_poll_thread(4), iterate:6

    ct_poll_thread(4), iterate:7

    ct_poll_thread(4), iterate:8

    ct_poll_thread(4), iterate:9

    ct_poll_thread(4), iterate:10

    ct_poll_thread(4), iterate:11

    ct_poll_thread(4), iterate:12

    ct_poll_thread(4), iterate:13

    ct_poll_thread(4), iterate:14

    ct_poll_thread(4), iterate:15

    ct_poll_thread(4), iterate:16

    ct_poll_thread(4), iterate:17

    ct_poll_thread(4), iterate:18

    ct_poll_thread(4), iterate:19

    ct_poll_thread(4), iterate:20

    ct_poll_thread(4), iterate:21

    ct_poll_thread(4), iterate:22

    ct_poll_thread(4), iterate:23

    ct_poll_thread(4), iterate:24

    ct_poll_thread(4), iterate:25

    ct_poll_thread(4), iterate:26

    ct_poll_thread(4), iterate:27

    ct_poll_thread(4), iterate:28

    ct_poll_thread(4), iterate:29

    ct_poll_thread(4), iterate:30

    ct_poll_thread(4), iterate:31

    ct_poll_thread(4), iterate:32

    ct_poll_thread(4), iterate:33

    ct_poll_thread(4), iterate:34

    ct_poll_thread(4), iterate:35

    ct_poll_thread(4), iterate:36

    ct_poll_thread(4), iterate:37

    ct_poll_thread(4), iterate:38

    ct_poll_thread(4), iterate:39

    ct_poll_thread(4), iterate:40

    ct_poll_thread(4), iterate:41
    ct_poll_thread(4), goodbye everybody from here! ;^o

    (4)->ct_per_thread::~ct_per_thread()
    ____________________

    Complete!
    ____________________

    ct_shared::~ct_shared()

    Fin!
    ____________________
    =====================================================

    Louis

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Wed Oct 30 19:17:42 2024
    From Newsgroup: comp.lang.c++

    On 10/30/24 17:06, Chris M. Thomasson wrote:
    On 10/30/2024 1:25 PM, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
         { a.lock() } -> std::same_as<void>;
         { a.unlock() } -> std::same_as<void>;

    Humm... For some reason it might be helpful for lock to return a epoch,
    or proxy_state thing or some sort of state. Then pass it into unlock.

        proxy_state lock();
        void unlock(proxy_state const&);

    Fwiw, I do this wrt my acquire and release functions, akin to your lock
    and unlock functions:

        collector& acquire()
        {
            [...]
        }

        void release(collector& c)
        {
            [...]
        }

    Humm...


    I use lock/unlock because RCU uses the BasicLocking requirement so std::scoped_lock can be used. c++ wasn't going to provide some
    king of scoped block for RCU like Java's synchronized block.
    C++ really really likes RAII a lot even though it logically
    creates a local variable whereas synchronized would not.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Oct 30 18:23:44 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 4:17 PM, jseigh wrote:
    On 10/30/24 17:06, Chris M. Thomasson wrote:
    On 10/30/2024 1:25 PM, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
         { a.lock() } -> std::same_as<void>;
         { a.unlock() } -> std::same_as<void>;

    Humm... For some reason it might be helpful for lock to return a
    epoch, or proxy_state thing or some sort of state. Then pass it into
    unlock.

         proxy_state lock();
         void unlock(proxy_state const&);

    Fwiw, I do this wrt my acquire and release functions, akin to your
    lock and unlock functions:

         collector& acquire()
         {
             [...]
         }

         void release(collector& c)
         {
             [...]
         }

    Humm...


    I use lock/unlock because RCU uses the BasicLocking requirement so std::scoped_lock can be used.  c++ wasn't going to provide some
    king of scoped block for RCU like Java's synchronized block.
    C++ really really likes RAII a lot even though it logically
    creates a local variable whereas synchronized would not.



    Ahhh. Fwiw, acquire and release in my api can be wrapped with raii logic
    for sure.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Anssi Saari@anssi.saari@usenet.mail.kapsi.fi to comp.lang.c++ on Thu Oct 31 11:47:59 2024
    From Newsgroup: comp.lang.c++

    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:

    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage.

    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort
    of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    Tried newer g++ and clang++ too but no. Joining those two lines, no
    problem. I don't think I wrapped that line either. And there's another
    one too.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c++ on Thu Oct 31 14:25:29 2024
    From Newsgroup: comp.lang.c++

    On 10/31/24 05:47, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    ...
    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort
    of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    You're attempting to create a string literal, but the syntax for string literals does not allow them to contain newline characters.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Thu Oct 31 18:43:18 2024
    From Newsgroup: comp.lang.c++

    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 10/31/24 05:47, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    ...
    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort
    of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    You're attempting to create a string literal, but the syntax for string >literals does not allow them to contain newline characters.


    Unless they're escaped.

    const char * string = "blah blah blah \
    blah blah blah";

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Oct 31 12:12:33 2024
    From Newsgroup: comp.lang.c++

    On 10/31/2024 2:47 AM, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:

    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage.

    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort
    of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    Tried newer g++ and clang++ too but no. Joining those two lines, no
    problem. I don't think I wrapped that line either. And there's another
    one too.

    Posting code in the newsreader can cause this: Sorry about that. Here
    are some screenshots of my current code:

    https://i.ibb.co/YtdKLfz/image.png

    https://i.ibb.co/DDnrdqL/image.png

    Not ready to be posted to github at this early stage.

    I just wanted to see if the ct_per_thread dtors were being called on
    other peoples systems.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Oct 31 12:47:14 2024
    From Newsgroup: comp.lang.c++

    On 10/31/2024 11:43 AM, Scott Lurndal wrote:
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 10/31/24 05:47, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    ...
    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort >>> of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    You're attempting to create a string literal, but the syntax for string
    literals does not allow them to contain newline characters.


    Unless they're escaped.

    const char * string = "blah blah blah \
    blah blah blah";


    Exactly. Fun with filling vertical space in the code itself:
    __________________
    #include <iostream>

    int main()
    {
    int a = 0;
    int b = 1;
    int c = 2;
    int d = 3;

    std::cout << "Dump: "
    << a << ", "
    << b << ", "
    << c << ", "
    << d;

    return 0;
    }

    __________________


    ;^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Fri Nov 1 23:39:36 2024
    From Newsgroup: comp.lang.c++

    On 31.10.2024 20:43, Scott Lurndal wrote:
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 10/31/24 05:47, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    ...
    Just curious, are line breaks inside strings OK in some environment? Or
    is that a language version thing? Here g++ 10.2.1 doesn't like this sort >>> of thing:

    std::cout << "ct_worker_thread(" << id << "), goodbye
    everybody from here! ;^o" << std::endl;

    You're attempting to create a string literal, but the syntax for string
    literals does not allow them to contain newline characters.


    Unless they're escaped.

    const char * string = "blah blah blah \
    blah blah blah";

    That's C style, and the result would not contain a line break. In C++ we
    have "raw" string literals which can contain embedded linebreaks.

    const char * string = R"__(blah blah blah
    blah blah blah)__";

    This comes pretty handy sometimes.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Muttley@Muttley@dastardlyhq.com to comp.lang.c++ on Sat Nov 2 09:51:58 2024
    From Newsgroup: comp.lang.c++

    On Fri, 1 Nov 2024 23:39:36 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 31.10.2024 20:43, Scott Lurndal wrote:
    Unless they're escaped.

    const char * string = "blah blah blah \
    blah blah blah";

    That's C style, and the result would not contain a line break. In C++ we

    Probably why K&R invented \n in the first place. Not sure why anyone would
    want to embed a raw linebreak in code.

    have "raw" string literals which can contain embedded linebreaks.

    const char * string = R"__(blah blah blah
    blah blah blah)__";

    This comes pretty handy sometimes.

    Hmm.

    fenris$ cat t.cc
    #include <stdio.h>

    int main()
    {
    const char *s = R"hello
    world";
    puts(s);
    }
    fenris$ c++ -std=c++20 t.cc
    t.cc:5:25: error: invalid character '
    ' character in raw string delimiter; use PREFIX( )PREFIX to delimit raw string
    5 | const char *s = R"hello
    | ^
    t.cc:5:18: error: expected expression
    5 | const char *s = R"hello
    | ^
    2 errors generated.
    fenris$ c++ -v
    Apple clang version 16.0.0 (clang-1600.0.26.3)
    Target: x86_64-apple-darwin24.0.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin


    Tried PREFIX, it didn't like that either. Oh well, not something I'll lose sleep over.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Sat Nov 2 17:16:26 2024
    From Newsgroup: comp.lang.c++

    On 02.11.2024 11:51, Muttley@dastardlyhq.com wrote:
    On Fri, 1 Nov 2024 23:39:36 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 31.10.2024 20:43, Scott Lurndal wrote:
    Unless they're escaped.

         const char * string = "blah blah blah \
    blah blah blah";

    That's C style, and the result would not contain a line break. In C++ we

    Probably why K&R invented \n in the first place. Not sure why anyone would want to embed a raw linebreak in code.

    have "raw" string literals which can contain embedded linebreaks.

          const char * string = R"__(blah blah blah
    blah blah blah)__";

    This comes pretty handy sometimes.

    Hmm.

    fenris$ cat t.cc
    #include <stdio.h>

    int main()
    {
        const char *s = R"hello
    world";
        puts(s);
    }
    fenris$ c++ -std=c++20 t.cc
    t.cc:5:25: error: invalid character '
    ' character in raw string delimiter; use PREFIX( )PREFIX to delimit raw string
       5 |         const char *s = R"hello
         |                                ^ t.cc:5:18: error: expected expression
       5 |         const char *s = R"hello
         |                         ^
    2 errors generated.
    fenris$ c++ -v
    Apple clang version 16.0.0 (clang-1600.0.26.3)
    Target: x86_64-apple-darwin24.0.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin


    Tried PREFIX, it didn't like that either. Oh well, not something I'll lose sleep over.

    You are missing the __( and )__ parts. Here:
    #include <stdio.h>

    int main() {
    const char* s = R"__(hello
    world)__";
    puts(s);
    }

    Example of real usage (depending on your newsreader, long lines may
    appear broken):

    } else if (testCase == "AwsLoadBalancer/hello") {
    // This is an example packet returned from AWS load balancer
    // Use fixed payload {"answer":"hello"}
    // Ensure some Expires fields are expired and some are not.
    std::string packet =
    R"__(HTTP/1.1 200 OK
    Date: Wed, 14 Feb 2024 13:17:22 GMT
    Content-Type: application/json
    Content-Length: 18
    Connection: close
    Set-Cookie: AWSALB=FuGpeJvy5vFGyKrgXnfvgcIlDljsF/MkBj4CCMxQJCqGiZTHCMaOG7wr9dwmkVbD8sWViwjcSPkYY3Gc7//OM/VhvXe386CEwLD0zBjJNjZXN5GTDI93Rj0y3F/K;
    Expires=Mon, 21 Feb 2124 13:17:22 GMT; Path=/
    Set-Cookie: AWSALBCORS=FuGpeJvy5vFGyKrgXnfvgcIlDljsF/MkBj4CCMxQJCqGiZTHCMaOG7wr9dwmkVbD8sWViwjcSPkYY3Gc7//OM/VhvXe386CEwLD0zBjJNjZXN5GTDI93Rj0y3F/K;
    Expires=Mon, 21 Feb 2124 13:17:22 GMT; Path=/; SameSite=None
    Set-Cookie: OLDALBCORS=FuGpeJvy5vFGyKrgXnfvgcIlDljsF/MkBj4CCMxQJCqGiZTHCMaOG7wr9dwmkVbD8sWViwjcSPkYY3Gc7//OM/VhvXe386CEwLD0zBjJNjZXN5GTDI93Rj0y3F/K;
    Expires=Wed, 14 Feb 2024 13:17:22 GMT; Path=/; SameSite=None
    server: uvicorn

    {"answer":"hello"})__";

    AddCRLF(packet);

    Yes, I could have composed it in a much harder way than by copy-paste
    from a debugger window, but why would I bother?



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c++ on Sat Nov 2 15:42:36 2024
    From Newsgroup: comp.lang.c++

    Muttley@dastardlyhq.com writes:
    On Fri, 1 Nov 2024 23:39:36 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 31.10.2024 20:43, Scott Lurndal wrote:
    Unless they're escaped.

    const char * string = "blah blah blah \
    blah blah blah";

    That's C style, and the result would not contain a line break. In C++ we

    Probably why K&R invented \n in the first place. Not sure why anyone would >want to embed a raw linebreak in code.

    Indeed. And the "C style" is perfectly legal
    and readable in both C and C++ code.

    There are certainly use cases for the C++ raw strings,
    if one is allowed to use C++11 or higher, but I see
    no problem using C strings when e.g. there is no need
    to embed utf-8/16/32 in the string.

    if (strcasecmp(argv[1], "help") == 0) {
    lp->log(
    "The Card Reader DLP supports a single punched card reader on\n"
    "Unit 0. The following control commands are supported:\n\n"
    " control %1$lu/0 status\n"
    " control %1$lu/0 load CARD-FILE-NAME\n\n"
    "CARD-FILE-NAME names a disk image file containing variable\n"
    "length ASCII records delimited by a single newline character.\n"
    "Each record will be treated as an image of a 80-column card\n"
    "punched using the Burruoghs EBCDIC encoding. Records containing\n"
    "less than 80 characters will be padded with EBCDIC space\n"
    "characters. A record more than 1 byte long that begins with\n"
    "an ASCII question mark character will be considered to have an\n"
    "invalid punch in column 1 and will be marked as a Control Card.\n"
    "Punched Card output files create by the CARD PUNCH\n"
    "DLP are formatted appropriately to be read by the reader DLP.\n\n"
    "Cards punched using Doug Jones hollerith (H80) disk file format\n"
    "will be recognized and converted to EBCDIC, BCL or BINARY\n"
    "formats as requested by the MCP.\n"
    , d_channel);
    return false;
    }
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Muttley@Muttley@dastardlyhq.com to comp.lang.c++ on Sat Nov 2 16:48:32 2024
    From Newsgroup: comp.lang.c++

    On Sat, 2 Nov 2024 17:16:26 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 02.11.2024 11:51, Muttley@dastardlyhq.com wrote:
    Tried PREFIX, it didn't like that either. Oh well, not something I'll lose >> sleep over.

    You are missing the __( and )__ parts. Here:
    #include <stdio.h>

    int main() {
    const char* s = R"__(hello
    world)__";
    puts(s);
    }

    Jesus christ, how much more contorted nonsense syntax can they come up with. Now there's compiler semantics inside a raw string?? FFS, this language is becoming a joke.

    Example of real usage (depending on your newsreader, long lines may
    appear broken):

    Rather contrived given it would be simple just to put \n on the end of each line. I still don't see the point.

    Yes, I could have composed it in a much harder way than by copy-paste
    from a debugger window, but why would I bother?

    Why would you cut and paste this stuff anyway into code?

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Sat Nov 2 12:00:12 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 3:01 PM, Louis Krupp wrote:
    On 10/29/2024 11:38 PM, Chris M. Thomasson wrote:
    Can anybody else compile and run this sucker? Thanks.

    I can, but I want some input for this early stage.
    _____________________________________________________

    <snip>
    _____________________________________________________

    Compiled clean with -Wall -Werror using:

        g++ (GCC) 14.2.1 20240912 (Red Hat 14.2.1-3).

    Output is:

    =====================================================
    [...]
    ct_poll_thread(4), iterate:41
    ct_poll_thread(4), goodbye everybody from here! ;^o

    (4)->ct_per_thread::~ct_per_thread()
    ____________________

    Complete!
    ____________________

    ct_shared::~ct_shared()

    Fin!
    ____________________
    =====================================================

    Thank you! I had a feeling it would work on GCC. I just tried it on a
    bunch of online compilers. The dtors are being called just fine.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Sat Nov 2 21:36:12 2024
    From Newsgroup: comp.lang.c++

    On 02.11.2024 18:48, Muttley@dastardlyhq.com wrote:
    On Sat, 2 Nov 2024 17:16:26 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 02.11.2024 11:51, Muttley@dastardlyhq.com wrote:
    Tried PREFIX, it didn't like that either. Oh well, not something I'll
    lose
    sleep over.

    You are missing the __( and )__ parts. Here:
    #include <stdio.h>

    int main() {
        const char* s = R"__(hello
            world)__";
            puts(s);
    }

    Jesus christ, how much more contorted nonsense syntax can they come up
    with.
    Now there's compiler semantics inside a raw string?? FFS, this language is becoming a joke.

    If you haven't figured this out, multicharacter (and potentially
    variable) delimiters are needed for unambiguous matching of the end of
    the string. This dates back to shell heredoc, and probably older, so not exactly a new invention.


    Example of real usage (depending on your newsreader, long lines may
    appear broken):

    Rather contrived given it would be simple just to put \n on the end of each line. I still don't see the point.

    Yes, I could have composed it in a much harder way than by copy-paste
    from a debugger window, but why would I bother?

    Why would you cut and paste this stuff anyway into code?

    This is just a random example of some test data for testing the app in
    nightly builds. I could have put it in a separate file or elsewhere, but again, this would be harder and more error prone, and the first rule of
    tests is that the tests themselves should be error free.

    And to me it is simpler to *not* put a \n on the end of each line and be
    done with it, rather than to put an \n (or more correctly, \r\n" or
    \r\n\) in the end of each line, and to find and escape all special
    characters in the string. YMMV.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Muttley@Muttley@dastardlyhq.com to comp.lang.c++ on Sun Nov 3 10:07:51 2024
    From Newsgroup: comp.lang.c++

    On Sat, 2 Nov 2024 21:36:12 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 02.11.2024 18:48, Muttley@dastardlyhq.com wrote:
    On Sat, 2 Nov 2024 17:16:26 +0200
    Paavo Helde <eesnimi@osa.pri.ee> gabbled:
    On 02.11.2024 11:51, Muttley@dastardlyhq.com wrote:
    Tried PREFIX, it didn't like that either. Oh well, not something I'll >>>> lose
    sleep over.

    You are missing the __( and )__ parts. Here:
    #include <stdio.h>

    int main() {
        const char* s = R"__(hello
            world)__";
            puts(s);
    }

    Jesus christ, how much more contorted nonsense syntax can they come up
    with.
    Now there's compiler semantics inside a raw string?? FFS, this language is >> becoming a joke.

    If you haven't figured this out, multicharacter (and potentially
    variable) delimiters are needed for unambiguous matching of the end of
    the string. This dates back to shell heredoc, and probably older, so not >exactly a new invention.

    Thats just word salad. R" that meant compile the following characters literally until the closing double quote would suffice. There's no need for compiler semantics inside a raw string on top of it being a hideous hack. Not that C++ is deficient in them as it is.

    Why would you cut and paste this stuff anyway into code?

    This is just a random example of some test data for testing the app in >nightly builds. I could have put it in a separate file or elsewhere, but

    You need a better testing setup.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Anssi Saari@anssi.saari@usenet.mail.kapsi.fi to comp.lang.c++ on Mon Nov 4 14:06:07 2024
    From Newsgroup: comp.lang.c++

    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:

    Posting code in the newsreader can cause this: Sorry about that. Here
    are some screenshots of my current code:

    https://i.ibb.co/YtdKLfz/image.png

    https://i.ibb.co/DDnrdqL/image.png

    OK, I thought you'd handle it. As in, as someone who posts code you
    would turn off line wrapping when posting code. But I guess Usenet
    doesn't integrate that well to modern code editors.

    Not ready to be posted to github at this early stage.

    Considering what all the broken crap that gets posted to github, it
    seems needlessly bashful.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Tue Nov 5 08:49:34 2024
    From Newsgroup: comp.lang.c++

    On 04.11.2024 14:06, Anssi Saari wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:

    Posting code in the newsreader can cause this: Sorry about that. Here
    are some screenshots of my current code:

    https://i.ibb.co/YtdKLfz/image.png

    https://i.ibb.co/DDnrdqL/image.png

    OK, I thought you'd handle it. As in, as someone who posts code you
    would turn off line wrapping when posting code. But I guess Usenet
    doesn't integrate that well to modern code editors.

    Given that my newsreader (Thunderbird 128.4.0esr) shows the lines in the original post from Chris correctly, I would not be so quick in blaming
    him or his tools.

    But in general it's true that broken lines in usenet postings are still
    a frequent problem. Just fix the lines by yourself as needed, if the
    issues have not been solved in 45 years there is little hope they will
    be solved now.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Anssi Saari@anssi.saari@usenet.mail.kapsi.fi to comp.lang.c++ on Fri Nov 8 14:01:31 2024
    From Newsgroup: comp.lang.c++

    Paavo Helde <eesnimi@osa.pri.ee> writes:

    Given that my newsreader (Thunderbird 128.4.0esr) shows the lines in
    the original post from Chris correctly, I would not be so quick in
    blaming him or his tools.

    You're right, it's my tool that's wrong. Or, well... With the MIME
    directive format=flowed there's only a hint that trailing whitespace on
    one line means the next line can be joined. Readers are still free to
    reformat such text as they please and what fits on a particular screen
    or window. At least that's how I understand RFC2646.

    Gnus here followed that so that it used matching indent when joining so code looked like this:

    std::cout << "ct_poll_thread(" << id << "), goodbye
    everybody from here! ;^o\n" << std::endl;

    This isn't valid C++ and the raw article isn't either.

    I guess if you compiled the code without having to edit it you
    copy-pasted it from TB?

    But in general it's true that broken lines in usenet postings are
    still a frequent problem. Just fix the lines by yourself as needed, if
    the issues have not been solved in 45 years there is little hope they
    will be solved now.

    I think this has been solved in many ways, people just aren't aware
    and/or not using the solutions.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Paavo Helde@eesnimi@osa.pri.ee to comp.lang.c++ on Sat Nov 9 09:07:30 2024
    From Newsgroup: comp.lang.c++

    On 08.11.2024 14:01, Anssi Saari wrote:
    Paavo Helde <eesnimi@osa.pri.ee> writes:

    Given that my newsreader (Thunderbird 128.4.0esr) shows the lines in
    the original post from Chris correctly, I would not be so quick in
    blaming him or his tools.

    You're right, it's my tool that's wrong. Or, well... With the MIME
    directive format=flowed there's only a hint that trailing whitespace on
    one line means the next line can be joined. Readers are still free to reformat such text as they please and what fits on a particular screen
    or window. At least that's how I understand RFC2646.

    Gnus here followed that so that it used matching indent when joining so code looked like this:

    std::cout << "ct_poll_thread(" << id << "), goodbye
    everybody from here! ;^o\n" << std::endl;

    This isn't valid C++ and the raw article isn't either.

    I guess if you compiled the code without having to edit it you
    copy-pasted it from TB?

    Yes, of course. Thunderbird also wraps this line if I make the window
    narrow enough, but this does not affect copy-paste, which comes out
    correct in any case.


    But in general it's true that broken lines in usenet postings are
    still a frequent problem. Just fix the lines by yourself as needed, if
    the issues have not been solved in 45 years there is little hope they
    will be solved now.

    I think this has been solved in many ways, people just aren't aware
    and/or not using the solutions.

    Then it's not solved.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Tue Nov 12 17:35:55 2024
    From Newsgroup: comp.lang.c++

    On 10/30/2024 4:17 PM, jseigh wrote:
    On 10/30/24 17:06, Chris M. Thomasson wrote:
    On 10/30/2024 1:25 PM, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
         { a.lock() } -> std::same_as<void>;
         { a.unlock() } -> std::same_as<void>;

    Humm... For some reason it might be helpful for lock to return a
    epoch, or proxy_state thing or some sort of state. Then pass it into
    unlock.

         proxy_state lock();
         void unlock(proxy_state const&);

    Fwiw, I do this wrt my acquire and release functions, akin to your
    lock and unlock functions:

         collector& acquire()
         {
             [...]
         }

         void release(collector& c)
         {
             [...]
         }

    Humm...


    I use lock/unlock because RCU uses the BasicLocking requirement so std::scoped_lock can be used.  c++ wasn't going to provide some
    king of scoped block for RCU like Java's synchronized block.
    C++ really really likes RAII a lot even though it logically
    creates a local variable whereas synchronized would not.



    This logic in my writer threads sort of shows how to use the proxy wrt lock-free and popping off of it and not causing havoc on the reader threads:

    // Writer threads
    // Mutates the lock free stack
    void ct_thread_writer(ct_shared& shared)
    {
    for (unsigned long wloop = 0; wloop < 42; ++wloop)
    {
    for (unsigned long i = 0; i < ct_writer_iters_n; ++i)
    {
    shared.m_stack.push(new ct_node());
    }

    std::this_thread::yield();

    ct_proxy_collector::collector& c = shared.m_proxy_gc.acquire();

    for (unsigned long i = 0; i < ct_writer_iters_n; ++i)
    {
    shared.m_proxy_gc.collect(c, shared.m_stack.pop());
    }

    shared.m_proxy_gc.release(c);

    std::this_thread::yield();

    if ((wloop % 3) == 0)
    {
    shared.m_proxy_gc.collect();
    }
    }
    }


    the reader is here:


    // Reader threads
    // Iterates through the lock free stack
    void ct_thread_reader(ct_shared& shared)
    {
    // iterate the lockfree stack
    for (unsigned long i = 0; i < ct_reader_iters_n; ++i)
    {
    ct_proxy_collector::collector& c = shared.m_proxy_gc.acquire();

    ct_node* n = shared.m_stack.get_head();

    while (n)
    {
    // need to add in some processing...
    //std::this_thread::yield();

    n = n->m_next.load(std::memory_order_relaxed);
    }

    shared.m_proxy_gc.release(c);
    }
    }



    No poll thread needed for this proxy! ;^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Wed Nov 13 13:43:24 2024
    From Newsgroup: comp.lang.c++

    On 10/30/24 16:25, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
        { a.lock() } -> std::same_as<void>;
        { a.unlock() } -> std::same_as<void>;
    };

    /**
     * @tparm T proxy collector type
     * @tparm B base object type for managed objects
     */
    template<typename T, typename B>
    concept ProxyType = requires(T proxy, B * obj)
    {
        BasicLockable<T>;
        std::has_virtual_destructor<B>::value;              // base object
    must have virtual destructor

        { proxy.unregister() } -> std::same_as<void>;       // unregister
    thread

        { proxy.retire(obj) } -> std::same_as<bool>;        // true if
    reclaim thread needs notification
        { proxy.retire_is_lockfree() } -> std::same_as<bool>;
        { proxy.retire_is_synchronous() } -> std::same_as<bool>;

        { proxy.try_reclaim() } -> std::same_as<bool>;      // false if no
    more pending retires

    };

    I did smrproxy and shared lock.  I didn't do arcproxy or
    any others.

    try_reclaim is  so you can run your own reclaim thread with
    retire and try_reclaim results to you could do notifies if
    necesary.
    retire_is_syncrhonous means no deferred retire so no
    try_reclaim is necessary.  Kind of moot.  If I go
    with rust I'll use rust traits.


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations. I may rethink
    making the proxy itself BasicLockable. Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Progress would be quicker but c++ templates
    turn IDEs back into dumb text editors.

    Joe Seigh
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Nov 13 13:04:39 2024
    From Newsgroup: comp.lang.c++

    On 11/13/2024 10:43 AM, jseigh wrote:
    On 10/30/24 16:25, jseigh wrote:
    On 10/30/24 15:30, Chris M. Thomasson wrote:


    This base will help me be able to test all sort of interesting proxy
    algorithms.


    This is a far as I got coming up with a proxy api

    template<typename T>
    concept BasicLockable = requires(T a)
    {
         { a.lock() } -> std::same_as<void>;
         { a.unlock() } -> std::same_as<void>;
    };

    /**
      * @tparm T proxy collector type
      * @tparm B base object type for managed objects
      */
    template<typename T, typename B>
    concept ProxyType = requires(T proxy, B * obj)
    {
         BasicLockable<T>;
         std::has_virtual_destructor<B>::value;              // base
    object must have virtual destructor

         { proxy.unregister() } -> std::same_as<void>;       // unregister
    thread

         { proxy.retire(obj) } -> std::same_as<bool>;        // true if
    reclaim thread needs notification
         { proxy.retire_is_lockfree() } -> std::same_as<bool>;
         { proxy.retire_is_synchronous() } -> std::same_as<bool>;

         { proxy.try_reclaim() } -> std::same_as<bool>;      // false if
    no more pending retires

    };

    I did smrproxy and shared lock.  I didn't do arcproxy or
    any others.

    try_reclaim is  so you can run your own reclaim thread with
    retire and try_reclaim results to you could do notifies if
    necesary.
    retire_is_syncrhonous means no deferred retire so no
    try_reclaim is necessary.  Kind of moot.  If I go
    with rust I'll use rust traits.


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations.  I may rethink
    making the proxy itself BasicLockable.  Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Humm... I have been experimenting with thread_local and it's working
    fine for my testing needs. I can get per-thread dtors, which is nice. At
    least they are working okay now. Back when I was first experimenting
    (years ago) with them, some of the dtors were not being called.

    Registering per-thread data with the polling thread is the tricky part. Lifetime issues. But, I have that solved for my testing needs.


    Progress would be quicker but c++ templates
    turn IDEs back into dumb text editors.

    lol.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Wed Nov 13 16:26:06 2024
    From Newsgroup: comp.lang.c++

    On 11/13/24 16:04, Chris M. Thomasson wrote:
    On 11/13/2024 10:43 AM, jseigh wrote:


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations.  I may rethink
    making the proxy itself BasicLockable.  Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Humm... I have been experimenting with thread_local and it's working
    fine for my testing needs. I can get per-thread dtors, which is nice. At least they are working okay now. Back when I was first experimenting
    (years ago) with them, some of the dtors were not being called.

    Registering per-thread data with the polling thread is the tricky part. Lifetime issues. But, I have that solved for my testing needs.


    It's not a question of making it work. It's awkward to use.
    If you are trying to dynamically create them, c++'s insistence
    on doing everything at compile time makes it more work than
    it has to be.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Wed Nov 13 13:38:16 2024
    From Newsgroup: comp.lang.c++

    On 11/13/2024 1:26 PM, jseigh wrote:
    On 11/13/24 16:04, Chris M. Thomasson wrote:
    On 11/13/2024 10:43 AM, jseigh wrote:


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations.  I may rethink
    making the proxy itself BasicLockable.  Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Humm... I have been experimenting with thread_local and it's working
    fine for my testing needs. I can get per-thread dtors, which is nice.
    At least they are working okay now. Back when I was first
    experimenting (years ago) with them, some of the dtors were not being
    called.

    Registering per-thread data with the polling thread is the tricky
    part. Lifetime issues. But, I have that solved for my testing needs.


    It's not a question of making it work.  It's awkward to use.
    If you are trying to dynamically create them, c++'s insistence
    on doing everything at compile time makes it more work than
    it has to be.

    Hard to disagree with that! Humm...

    Is this a massive hack?


    thread_local struct ct_per_thread* ct_g_per_thread = nullptr;

    _______________
    struct ct_per_thread
    {
    ct_shared& m_shared;
    unsigned long m_id;
    std::mutex m_locks[CT_PER_THREAD_LOCKS_N];

    ct_per_thread(
    ct_shared& shared,
    unsigned long id
    ): m_shared(shared),
    m_id(id)
    {
    [...]
    }

    ~ct_per_thread()
    {
    [...]
    }
    };
    _______________


    A thread that sets its per thread data, it gives it access to the shared
    data. id is there for testing purposes.
    _______________
    // Humm...
    // It should work fine in modern C++! :^)
    {
    thread_local ct_per_thread l_ct_per_thread(shared, id);
    ct_g_per_thread = &l_ct_per_thread;
    }
    _______________

    I can call the ctor for the perthread thread local and pass it vital
    info. Giving it access to every function the thread can call via ct_g_per_thread. access to the shared data is key. The dtor can remove
    itself from the polling thread via shared. Not sure if it's a hack, but
    it's working just for my testing program.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Thu Nov 14 09:06:23 2024
    From Newsgroup: comp.lang.c++

    On 11/13/24 16:38, Chris M. Thomasson wrote:
    On 11/13/2024 1:26 PM, jseigh wrote:
    On 11/13/24 16:04, Chris M. Thomasson wrote:
    On 11/13/2024 10:43 AM, jseigh wrote:


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations.  I may rethink
    making the proxy itself BasicLockable.  Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Humm... I have been experimenting with thread_local and it's working
    fine for my testing needs. I can get per-thread dtors, which is nice.
    At least they are working okay now. Back when I was first
    experimenting (years ago) with them, some of the dtors were not being
    called.

    Registering per-thread data with the polling thread is the tricky
    part. Lifetime issues. But, I have that solved for my testing needs.


    It's not a question of making it work.  It's awkward to use.
    If you are trying to dynamically create them, c++'s insistence
    on doing everything at compile time makes it more work than
    it has to be.

    Hard to disagree with that! Humm...

    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    thread_local struct ct_per_thread* ct_g_per_thread = nullptr;

    ...

    I had considered the preallocated array but you have to guess
    what is a reasonable size, have logic for reusing ids/keys,
    and if you run out of slots, you have a runtime error the
    apps have to deal with. Basically your proxy ctor would have
    to throw an exception if no slots were available. Same as
    if pthread_key_create returned an error.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Nov 14 14:08:45 2024
    From Newsgroup: comp.lang.c++

    On 11/14/2024 6:06 AM, jseigh wrote:
    On 11/13/24 16:38, Chris M. Thomasson wrote:
    On 11/13/2024 1:26 PM, jseigh wrote:
    On 11/13/24 16:04, Chris M. Thomasson wrote:
    On 11/13/2024 10:43 AM, jseigh wrote:


    I have arcproxy mostly ported to c++ so that's 4
    different proxy implementations.  I may rethink
    making the proxy itself BasicLockable.  Using
    thread_local tends to turn everything into a
    singleton unless one resorts to using some hack.

    Humm... I have been experimenting with thread_local and it's working
    fine for my testing needs. I can get per-thread dtors, which is
    nice. At least they are working okay now. Back when I was first
    experimenting (years ago) with them, some of the dtors were not
    being called.

    Registering per-thread data with the polling thread is the tricky
    part. Lifetime issues. But, I have that solved for my testing needs.


    It's not a question of making it work.  It's awkward to use.
    If you are trying to dynamically create them, c++'s insistence
    on doing everything at compile time makes it more work than
    it has to be.

    Hard to disagree with that! Humm...

    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    thread_local struct ct_per_thread* ct_g_per_thread = nullptr;

    ...

    I had considered the preallocated array but you have to guess
    what is a reasonable size, have logic for reusing ids/keys,
    and if you run out of slots, you have a runtime error the
    apps have to deal with.  Basically your proxy ctor would have
    to throw an exception if no slots were available.  Same as
    if pthread_key_create returned an error.

    Need to ponder on this. So, are you saying that no calls to
    pthread_key_delete would be made and eventually it would run out of
    slots? PTHREADS_KEY_MAX? Need to think here.

    Although, iirc one key can handle multiple threads via:

    pthread_setspecific
    pthread_getspecific

    So, my scheme would require two of them for the lifetime of the program?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Thu Nov 14 20:08:07 2024
    From Newsgroup: comp.lang.c++

    On 11/14/24 17:08, Chris M. Thomasson wrote:
    On 11/14/2024 6:06 AM, jseigh wrote:


    I had considered the preallocated array but you have to guess
    what is a reasonable size, have logic for reusing ids/keys,
    and if you run out of slots, you have a runtime error the
    apps have to deal with.  Basically your proxy ctor would have
    to throw an exception if no slots were available.  Same as
    if pthread_key_create returned an error.

    Need to ponder on this. So, are you saying that no calls to pthread_key_delete would be made and eventually it would run out of
    slots? PTHREADS_KEY_MAX? Need to think here.

    The thread specific storage segment is fixed in size, the
    sum of all the thread_local declares. So pthreads has to
    reserve how many slots it thinks it needs.


    Although, iirc one key can handle multiple threads via:

    pthread_setspecific
    pthread_getspecific

    So, my scheme would require two of them for the lifetime of the program?

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Nov 14 21:10:38 2024
    From Newsgroup: comp.lang.c++

    On 11/14/2024 5:08 PM, jseigh wrote:
    On 11/14/24 17:08, Chris M. Thomasson wrote:
    On 11/14/2024 6:06 AM, jseigh wrote:


    I had considered the preallocated array but you have to guess
    what is a reasonable size, have logic for reusing ids/keys,
    and if you run out of slots, you have a runtime error the
    apps have to deal with.  Basically your proxy ctor would have
    to throw an exception if no slots were available.  Same as
    if pthread_key_create returned an error.

    Need to ponder on this. So, are you saying that no calls to
    pthread_key_delete would be made and eventually it would run out of
    slots? PTHREADS_KEY_MAX? Need to think here.

    The thread specific storage segment is fixed in size, the
    sum of all the thread_local declares. So pthreads has to
    reserve how many slots it thinks it needs.

    Well, humm... In my test harness, well, I am using two thread_local's,
    that should be well below the "thereshold" where throwing could occur
    wrt slots?... In this limited test realm, it should be okay?



    Although, iirc one key can handle multiple threads via:

    pthread_setspecific
    pthread_getspecific

    So, my scheme would require two of them for the lifetime of the program?


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Nov 14 21:14:10 2024
    From Newsgroup: comp.lang.c++

    On 11/13/2024 1:04 PM, Chris M. Thomasson wrote:
    On 11/13/2024 10:43 AM, jseigh wrote:
    [...]
    Progress would be quicker but c++ templates
    turn IDEs back into dumb text editors.

    lol.

    Examine those long and highly verbose template errors, reams of them...
    Fun times.... wow.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Sat Nov 16 09:25:23 2024
    From Newsgroup: comp.lang.c++

    On 11/14/24 09:06, jseigh wrote:
    On 11/13/24 16:38, Chris M. Thomasson wrote:


    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    So if you want to program dangerously.

    Basically your class declares a thread_local which
    would normally make it a singleton class effectively.

    You can create other non singleton instances by
    having a constructor which takes a locally declared
    thread_local passed in a a ctor argument. You
    then take the difference between that and the
    "singleton" thread_local and use that as an array
    index to access the appropriate thread_local.
    So "dynamic" thread_locals without having to
    guess at how many preallocated thread locals
    to create

    thread_local int base; // used for singleton instance


    thread_local int other; // used for some other instance


    int ndx = &base - &other; // stored in instance to access instance
    // tls

    (&base)[ndx] ... // access the instance tls

    Don't store or precalculate &base. It will be different for every
    thread. There's no portable way to validate an address to see if
    it is a tls address.

    Joe Seigh


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Sat Nov 16 13:01:17 2024
    From Newsgroup: comp.lang.c++

    On 11/16/2024 6:25 AM, jseigh wrote:
    On 11/14/24 09:06, jseigh wrote:
    On 11/13/24 16:38, Chris M. Thomasson wrote:


    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    So if you want to program dangerously.

    Basically your class declares a thread_local which
    would normally make it a singleton class effectively.


    You can create other non singleton instances by
    having a constructor which takes a locally declared
    thread_local passed in a a ctor argument.  You
    then take the difference between that and the
    "singleton" thread_local and use that as an array
    index to access the appropriate thread_local.
    So "dynamic" thread_locals without having to
    guess at how many preallocated thread locals
    to create

    One key for the lifetime of the program, ala the old school pthread way, and/or the perfectly standard C11 tss_create way:

    void program_entry()
    {
    // create pthread_key_t / tss_t

    {
    // testing 123... ;^)
    // run many multi threaded tests...
    }

    // destroy pthread_key_t / tss_t
    }

    using my thread_local "thing" for my personal testing seems to be
    working just fine for me personally. I HOPE that the thread_locals are
    only using two keys no matter how many threads are created and joined...
    I have to use two of them wrt the global pointer and the per-thread
    instance hack to get the C++11 thread_local to work for my purposes... Humm.... Dr hack needs to ask his brother dr foobar about it.


    The global one:
    _______________________
    thread_local struct ct_per_thread* ct_g_per_thread = nullptr; _______________________



    the thread local one:
    _______________________
    // Humm...
    // It should work fine in modern C++! :^)
    {
    thread_local ct_per_thread l_ct_per_thread(shared, id);
    ct_g_per_thread = &l_ct_per_thread;
    }
    _______________________


    this should create only two keys for the entire lifetime of the program,
    no matter how many threads it creates and joins during its testing? If I
    am mistaken here, well, that would suck!

    Using tss_create/delete is always an option....

    ;^o



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Sun Nov 17 08:50:20 2024
    From Newsgroup: comp.lang.c++

    On 11/16/24 09:25, jseigh wrote:
    On 11/14/24 09:06, jseigh wrote:
    On 11/13/24 16:38, Chris M. Thomasson wrote:


    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    So if you want to program dangerously.

    Basically your class declares a thread_local which
    would normally make it a singleton class effectively.

    You can create other non singleton instances by
    having a constructor which takes a locally declared
    thread_local passed in a a ctor argument.  You
    then take the difference between that and the
    "singleton" thread_local and use that as an array
    index to access the appropriate thread_local.
    So "dynamic" thread_locals without having to
    guess at how many preallocated thread locals
    to create

      thread_local int base;    // used for singleton instance


      thread_local int other;       // used for some other instance


      int ndx = &base - &other;     // stored in instance to access instance
                                    // tls

      (&base)[ndx] ...              // access the instance tls

    Don't store or precalculate &base.  It will be different for every
    thread.  There's no portable way to validate an address to see if
    it is a tls address.


    Note, this is only for types that the alignment is same as size.
    Everything else you will probably need to cast to char*, calculate
    the offset, and cast back after adjustment.

    Joe Seigh
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c++ on Sun Nov 17 10:42:10 2024
    From Newsgroup: comp.lang.c++

    On 11/16/24 09:25, jseigh wrote:
    On 11/14/24 09:06, jseigh wrote:
    On 11/13/24 16:38, Chris M. Thomasson wrote:


    Is this a massive hack?

    No, fairly simple but potentially unsafe. Very unsafe :)


    So if you want to program dangerously.

    Basically your class declares a thread_local which
    would normally make it a singleton class effectively.

    You can create other non singleton instances by
    having a constructor which takes a locally declared
    thread_local passed in a a ctor argument. You
    then take the difference between that and the
    "singleton" thread_local and use that as an array
    index to access the appropriate thread_local.
    So "dynamic" thread_locals without having to
    guess at how many preallocated thread locals
    to create

    thread_local int base; // used for singleton instance


    thread_local int other; // used for some other instance


    int ndx = &base - &other; // stored in instance to access instance

    The behavior of that subtraction is undefined (7.6.6p5.3). If the
    documentation for the implementation you are using defines what the
    result will be, you can make use of it, but such code is unportable.

    If you are going to write such code, the result of that difference is of
    type ptrdiff_t, which is guaranteed to be big enough to be used for this purpose, which is not true of int. Even if 'int' is big enough, if it's
    not the same as ptrdiff_t, using an 'int' causes an unnecessary conversion.

    // tls

    (&base)[ndx] ... // access the instance tls

    For purposes of pointer arithmetic, '&base' is treated as pointing at an
    array of length 1 of int. Using any non-zero value as a subscript has
    undefined behavior.

    --- Synchronet 3.20a-Linux NewsLink 1.114