0

I have two threads. The first creates a Logic object, detaching a second thread to spin, blocking on OpenSSL socket to receive messages:

struct Logic
{
    Logic()
    {
        std::thread t1(&Logic::run, this);
        t1.detach();
    }

    void run()
    {
        while(true)
        {
            // Gets data from SSL (blocking socket)
            // Processes data
            // Updates timestamp
        }
    }

    uint64_t timestamp;
};

The first thread returns, enters a while loop and continually checks if the detached thread is still running (or whether its blocked permanently).

while(true)
{
    Logic logic();
    while(true)
    {
        if(timestamp_not_updated)
        {
            break; // Break, destroy current Logic object and create another
        }
    }
}

If the timestamp stops being updated, the inner while loop breaks, causing the Logic object to be destroyed and a new one created.

When this restart behaviour triggers I get a seg fault. thread apply all bt shows 3 threads, not 2. The original detached thread (blocking on OpenSSL) still exists. I thought this would get destroyed due to the object.

How do I stop a detached thread which is blocking/waiting on a resource, so I can restart my class? I need the blocking behaviour because I don't have anything else to do (besides receive the packet) and it's better for performance, than to keep calling in to OpenSSL.

intrigued_66
  • 16,082
  • 51
  • 118
  • 189
  • The only robust solution I've found for this problem is to rewrite the thread's networking code to use excusively non-blocking I/O instead (which can be done for OpenSSL, although it is painful and difficult to do correctly). That way the thread is guaranteed only to block inside `select()` or `poll()` (or similar), and your main thread can cause network thread's select()/poll()/etc call to return at any time e.g. by writing a byte to a pipe that the call is monitoring for read-ready. – Jeremy Friesner Oct 01 '22 at 04:07
  • @JeremyFriesner ahhhhh I really can't use non-blocking because the latency I lose from continually calling in to OpenSSL, back out, then back in..... continually........ would be a lot – intrigued_66 Oct 01 '22 at 04:09
  • 1
    That's what the `select()` call is for, to keep you from constantly busy-looping. If done correctly, it's no less efficient than blocking I/O (just more complicated) – Jeremy Friesner Oct 01 '22 at 04:10
  • AFAIR `select` has a timeout for this so you can check if the thread should be stop when the timeout is reached. I remember that you can know when a timeout is reach so you do not need to iterate over the sockets in this case. – Jérôme Richard Oct 01 '22 at 23:36
  • The destructor of std::thread calls std::terminate if the thread is still running when the object goes out of scope. You can detach the thread, but when the program exits, the thread won't do any stack unwinding. Better practice is to join it. No one can answer your question about how to break the blocking without knowing exactly what objects / functions you are calling, so please include that. If it's anything like POSIX sockets, Linux will unblock if you shutdown and close the socket from a different thread. This is Linux behavior (not guaranteed by POSIX) for FDs created by `socket`. – Humphrey Winnebago Oct 03 '22 at 05:03
  • @HumphreyWinnebago I ended-up taking the native handle from std thread and calling pthread_cancel. However, the cancelled thread DID stack-unwind, causing sanitize to detect illegal writes. – intrigued_66 Oct 04 '22 at 22:52
  • Interesting. See this answer https://stackoverflow.com/a/38819432/9107647. Sounds like it's unspecified or UB territory. Probably fine for a toy example, though – Humphrey Winnebago Oct 04 '22 at 23:54
  • BTW this problem of unblocking waiting sockets is common but the best solutions I've heard were unbelievably janky. 1) Switch to using `select` or `poll` and then write to yourself. If it's a blocking accept, just send a connection to yourself. 2) rewrite your code to be async. Like I said, I found that calling `shutdown` and `close` on the socket in Linux will unblock all blocking operations on other threads. (Watch out for race conditions on the `close`. i.e. don't call close twice on the same FD) – Humphrey Winnebago Oct 05 '22 at 00:01

0 Answers0