3

I'd like to pass a string argument to a child thread ( that is continuously reading a socket ) and call a setsockopt() with that argument on that socket.

I'm using ZeroMQ sockets, so calling setsockopt() is not threadsafe here, I'd call the setsockopt() from the child thread ( as was recommended here ). An argument update would occur probably only once in billions of read cycles, and it feels a bit wrong to add an if-structure to the child's every cycle like this:

bool new_arg_available;
std::string new_arg;

while(1){
    sub_socket->recv(data);   // . . . . . . a blocking method call
    printData(data)

    ...                       // . . . . . . data can set new_arg_available

    if (new_arg_available){   // . . . . . . synchronization goes here
        sub_socket->setsockopt(ZMQ_SUBSCRIBE, new_arg, ... );
        new_arg_available = false;
    }
}

For me, the most straightforward would probably be either:

  1. Add a mutex to the global namespace, lock it when a new_arg available in the parent thread, and unlock it from the child thread.

  2. use a std::atomic<bool> and use it the same way as in 1.

However, what I would like to achieve somehow is to make the child interruptible, so that I could eliminate the if-structure from the end of the while(){...} block. I wouldn't mind the penalty of a context switch, because this event would be so seldom.

I'm relatively new to C++, and I would like to learn about best practices here, how to achieve this in an efficient way. I would like to solve it without using Boost, the only examples I could find to achieve interruptible threads were using Boost.

user3666197
  • 1
  • 6
  • 50
  • 92
janos_dfc
  • 87
  • 1
  • 5
  • When a resource is shared among threads you need mutexes. One in the "write" operation, and other in the "read" operation. Unlock each mutext as soon as the operation is done. – Ripi2 Jun 25 '18 at 18:30
  • I am really curious about an answer here too, I cannot think of a way that doesn't poll a boolean in modern c++. – arynaq Jun 25 '18 at 18:44
  • 1
    I wouldn't worry about the additional `if`. You could even use some compiler specific construct to specify that the branch is `unlikely`. Another solution would be to have a dedicated socket to pass `new_arg` over. In that case, instead of `sub_socket->recv()` you'd use a `poller` to poll both `sub_socket` and your `new_param_receiver_socket`. – Xaqq Jun 25 '18 at 21:19

1 Answers1

2

Welcome to the land of Zen-of-Zero

The primary remark on the ZeroMQ evangelisation is, that the ZeroMQ authors have advocated since ever to avoid any sort of sharing - Zero-sharing.

Given you already have instantiated the ZeroMQ infrastructure, better use an inter-thread PUSH/PULL over the IO-thread-less inproc:// transport-class and forget about any tricky (b-)locking.

The one side ( the Injector ) just aPushCHANNEL.send( new_arg );
and the other ( the Implementor ) just either aPullCHANNEL.recv()-s as needed / when needed, or may use smarter means of aPullCHANNEL.poll()-testing, again best if using the Zero-wait form of the polling-test if aPullCHANNEL.poll( 0 ){...}else{...}

And your architecture becomes a fully decomposed, multi-agent role-based clean and smart, without having any dirty deadlocks ( so best forget REQ/REP since beginning ), without any hard to trace/debug conceptual flaws and all errors get detected where applicable and where a local responsibility makes sense to map the root-cause of any such error for an on-the-fly due error-handling.


NB: Yes, ZeroMQ has been since ever designed without a need for a thread-safeness, as there was nothing left to be shared ( Zero-sharing ). Some recent efforts in API v4.1+ started to decline from this Zen-of-Zero, so one may read about some aspects of thread-safeness, yet as noted above and as present on almost every page of the fabulous Pieter HINTJENS' ZeroMQ bible-book - "Code Connected, Volume 1" - best share nothing.

The ZeroMQ design is fully asynchronous and requires no care for locking, mutex-gymnastics and other forms of extrinsically introduced elements in an otherwise clean and smart multi-agent async signalling / messaging based architecture. You will fall in love with it, so keep trying and read the book. It will be hard, yet it will pay a lot for those, who will carry on and find the beauty of ZeroMQ Zen-of-Zero.

halfer
  • 19,824
  • 17
  • 99
  • 186
user3666197
  • 1
  • 6
  • 50
  • 92
  • Thank you! I understand that the more complex a zmq design gets with more sockets, the more complicated it becomes to handle them in separate threads, now I can see I'm better off with poll. In the design above I can easily create a "message deadlock" by unsubscribing from all the messages and then get stuck in the blocking recv() in that thread. – janos_dfc Jun 28 '18 at 07:57