1

With ZeroMQ and CPPZMQ 4.3.2, I want to drop old messages for all my sockets including

  • PAIR
  • Pub/Sub
  • REQ/REP

So I use m_socks[channel].setsockopt(ZMQ_CONFLATE, 1) on all my sockets before binding/connecting.

Test

However, when I made the following test, it seems that the old messages are still flushed out on each reconnection. In this test,

  • I use a thread to keep sending generated sinewave to a receiver thread
  • Every 10 seconds I double the sinewave's frequency
  • Then after 10 seconds I stop the process

Below is the pseudocode of the sender

// on sender end

auto thenSec = high_resolution_clock::now();
while(m_isRunning) {

    // generate sinewave, double the frequency every 10s or so

    auto nowSec = high_resolution_clock::now();
    if (duration_cast<seconds>(nowSec - thenSec).count() > 10) {
        m_sine.SetFreq(m_sine.GetFreq()*2);
        thenSec = nowSec;
    }
    m_sine.Generate(audio);

    // send to rendering thread

    m_messenger.send("inproc://sound-ear.pair", 
        (const void*)(audio), 
        audio_size, 
        zmq::send_flags::dontwait
    );

}

Note that I already use DONTWAIT to mitigate blocking.

On the receiver side I have a zmq::poller_event handler that simply receives the last message on event polling.

In the stop sequence I reset the sinewave frequency to its lowest value, say, 440Hz.

Expected

The expected behaviour would be:

  • If I stop both the sender and the receiver after 10s when the frequency is doubled,
  • and I restart both,
  • then I should see the sinewave reset to 440Hz.

Observed

But the observed behaviour is that the received sinewave is still of the doubled frequency after restarting the communication, i.e., 880Hz.

Question

Am I doing it wrong or should I use some kind of killswitch to force drop all messages in this case?

kakyo
  • 10,460
  • 14
  • 76
  • 140

1 Answers1

0

OK, I think I solved it myself. Kind of.

Actual solution

I finally realized that the behaviour I want is to flush all messages when I stop the rendering. According to the official doc(How can I flush all messages that are in the ZeroMQ socket queue?), this can only be achieved by

  • set the sockets of both sender's and receiver's ZMQ_LINGER option to 0, meaning to keep nothing on closing those sockets;
  • closing the sockets on both sender and receiver ends, which also involves bootstrapping pollers and all references to the sockets.

This seems a lot of unnecessary work if I'm to restart rendering my data again, right after the stop sequence. But I found no other way to solve this cleanly.

Initial effort

It seems to me that ZMQ_CONFLATE does not make a difference on PAIR. I really have to tweak high water marks on sender and receiver ends using ZMQ_SNDHWM and ZMQ_RCVHWM.

However, I said "kind of solved" because tweaking HWM in the end is not the optimal solution for a realtime application,

  • having ZMQ_SNDHWM / ZMQ_RCVHWM set to the minimum "1", we still have a sizable latency in terms of realtime.

  • Also, the consumer thread could fall into underrun situatioin, i.e., perceivable jitters with the lowest HWM.

If I'm not doing anything wrong, I guess the optimal solution would still be shared memory for my targeted scenario. This is sad because I really enjoyed the simplicity of ZMQ's multicast messaging patternsand hate to deal with thread locking littered everywhere.

kakyo
  • 10,460
  • 14
  • 76
  • 140