RabbitMQ C++ Client consumer_cancel_notify - rabbitmq

Following the guidelines here I'm able to set the "consumer_cancel_notify" property for my client connection, but when the Queue is deleted the client still isn't noticing. I'm guessing that I probably have to override some method or set a callback somewhere, but after digging through the source code I'm lost as to where I'd do this. Does anybody offhand know where I'd listen for this notification?

Ok here's how I got it to work:
When creating the Queue (i.e. "declaring" the Queue), add a callback for the "AMQP_CANCEL" messages.
Inside AMQPQueue::sendConsumeCommand(), inside the while(1) loop where the code checks for the different *frame.payload.method.id*s, add a check for the AMQP_BASIC_CANCEL_METHOD, e.g.
if (frame.payload.method.id == AMQP_BASIC_CANCEL_METHOD){
cout << "AMQP_BASIC_CANCEL_METHOD received" << endl;
if ( events.find(AMQP_CANCEL) != events.end() ) {
(*events[AMQP_CANCEL])(pmessage);
}
continue;
}
That's it.
For my purposes I wanted to redeclare the Queue if it got deleted so that I could keep consuming messages, so inside my callback I just redeclared the queue, set up the bindings, added events, set the consumer tag, and consumed.

Related

Why is Flow created on ConflatedBroadcastChannel only able to receive last element?

The following code only prints 10000 i.e. only the last element
val channel = BroadcastChannel<Int>(Channel.CONFLATED)
val flowJob = channel.asFlow().buffer(Channel.UNLIMITED).onEach {
println(it)
}.launchIn(GlobalScope)
for (i in 0..100) {
channel.offer(i*i)
}
flowJob.join()
Code can be ran in the playground.
But since the Flow is launched in separate dispatching thread, and value is sent to the Channel and since Flow has an unlimited buffer, it should receive each element till onEach is invoked. But why only the last element is able to get received?
Is this the expected behavior or some bug? If its expected behavior how would somebody try to push only the newest elements to the flow, but all the flow that has certain buffer can receive the element.
Actually, this is about the "Conflate" way of buffering. For buffering a flow you have a couple of ways such as using buffer() method or collectLatest() or conflate(). Each of them has their own way to buffer. So conflate() method's way is that when the flow emits values, it tries to collect but when the collector is too slow, then conflate() skips the intermediate values for the sake of the flow. And it's doing it even tho every time it's emitted in a separate coroutine. So in a channel, a similar thing is happening basically.
Here is the official doc explanation:
When a flow represents partial results of the operation or operation
status updates, it may not be necessary to process each value, but
instead, only most recent ones. In this case, the conflate operator
can be used to skip intermediate values when a collector is too slow
to process them.
Check out this link.
The explanation is for flow but you need to focus on the feature that you are using. And in this case, conflation is same for channel and flow.
The problem here is the Channel.CONFLATED. Taken from the docs:
Channel that buffers at most one element and conflates all subsequent `send` and `offer` invocations,
so that the receiver always gets the most recently sent element.
Back-to-send sent elements are _conflated_ -- only the the most recently sent element is received,
while previously sent elements **are lost**.
Sender to this channel never suspends and [offer] always returns `true`.
This channel is created by `Channel(Channel.CONFLATED)` factory function invocation.
This implementation is fully lock-free.
so this is why you only get the most recent (last) element. I'd use an UNLIMITED Channel instead:
val channel = Channel<Int>(Channel.UNLIMITED)
val flowJob = channel.consumeAsFlow().onEach {
println(it)
}.launchIn(GlobalScope)
for (i in 0..100) {
channel.offer(i*i)
}
flowJob.join()
As some of the comments stated, using Channel.CONFLATED will store only the last value, and you are offering to the channel, even if your flow has a buffer.
Also join() will suspend until the Job is not complete, in your case infinitely, that's why you needed the timeout.
val channel = Channel<Int>(Channel.RENDEZVOUS)
val flowJob = channel.consumeAsFlow().onEach {
println(it)
}.launchIn(GlobalScope)
GlobalScope.launch{
for (i in 0..100) {
channel.send(i * i)
}
channel.close()
}
flowJob.join()
Check out this solution (playground link), with the Channel.RENDEZVOUS your channel will accept new elements only if the others are already consumed.
This is why we have to use send instead of offer, send suspends until it can send elements, while offer returns a boolean indicating if send was succesfull.
At last, we have to close the channel, in order for join() not to suspend until eternity.

If it safe to have blocking operation inside flatMap { ... } mapper function?

I'd like to organize a thread barrier: given a single lock object, any thread can obtain it and continue thread's chain further, but any other thread will stay dormant on the same lock object until the first thread finishes and releases the lock.
Let's express my intention in code (log() simply prints string in a log):
val mutex = Semaphore(1) // number of permits is 1
source
.subscribeOn(Schedulers.newThread()) // any unbound scheduler (io, newThread)
.flatMap {
log("#1")
mutex.acquireUninterruptibly()
log("#2")
innerSource
.doOnSubscribe(log("#3"))
.doFinally {
mutex.release()
log("#4")
}
}
.subscribe()
It actually works well, i can see how multiple threads show log "#1" and only one of them propagates further, obtaining lock object mutex, then it releases it and i can see other logs, and next threads comes into play. OK
But sometimes, when pressure is quite high and number of threads is greater, say 4-5, i experience DEADLOCK:
Actually, the thread that has acquired the lock, prints "#1" and "#2" but it then never print "#3" (so doOnSubscribe() not called), so it actually stops and does nothing, not subscribing to innerSource in flatMap. So all threads are blocked and app is not responsive at all.
My question - is it safe to have blocking operation inside flatMap? I dig into flatMap source code and i see the place where it internally subscribes:
if (!isDisposed()) {
o.subscribe(new FlatMapSingleObserver<R>(this, downstream));
}
Is it possible that thread's subscription, that has acquired lock, was disposed somehow?
You can use flatMap second parameter maxConcurrency and set it to 1, so it does what you want without manually locking

WebRTC PeerConnectionFactory Graceful Shutdown

I'm writing a native WebRTC application that forwards pre-encoded frames for a client right now. That part is all fine, but I'm having segfault issues every time I attempt to exit my application, specifically with regards to how I'm destroying my WebRtcPeerConnectionFactory.
I'm instantiating this object by first launching separate threads for networking, signaling, and working respectively, and in my destructor kill these threads by calling thread->Quit() before setting my webRtcPeerConnectionFactory to a nullptr (as I've seen in examples in the source code do in their conductor.cc files), but I either segfault or hang indefinitely depending on the order with which the prior two actions are taken.
On a high level is there a correct way to gracefully destroy the factory object or is there some cleanup function I'm not calling? I can't find any other examples online that take advantage of the WebRTC threading model so I'm not sure where to move on from here. Thanks!
My instantiation of the object is performed like so:
rtc_network_thread_ = rtc::Thread::CreateWithSocketServer();
rtc_worker_thread_ = rtc::Thread::Create();
rtc_signaling_thread_ = rtc::Thread::Create();
if (!rtc_network_thread_->Start() || !rtc_worker_thread_->Start() || !rtc_signaling_thread->Start()) {
// error handling
}
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
rtc_network_thread_.get(), rtc_worker_thread_.get(), rtc_signaling_thread_.get(),
nullptr, webrtc::CreateBuiltInAudioEncoderFactory(), webrtc::CreateBuiltInAudioDecoderFactory(),
dummy_encoder_factory_.get(), nullptr)
And my subsequent cleanup looks like this:
rtc_worker_thread_->Quit();
rtc_network_thread_->Quit();
rtc_signaling_thread_->Quit();
if (peer_connection_factory_) {
// errors occur here, either with setting to nullptr or with threads
// quitting if I quit the threads after setting my factory to a nullptr
peer_connection_factory_ = nullptr;
}

How to access CAN signals dynamically (by string) in CAPL?

I'm trying to force CAN signals to given values using COM interface of CANalyzer. Since there is no COM method to send CAN messages, I'm implementing a workaround using CAPL:
void SendMySignal(int value) {
message MyMessage msg;
msg.MySignal = value;
output(msg);
}
This works fine, however since MyMessage and MySignal are referenced statically (by name) here, I'll have to implement N functions to be able to send N signals (or an N-way switch statement, etc). Is there a way to avoid the hassle and access signals inside a message by string? Something like this:
void SendSignal(int MessageID, char SignalName, int value)
I'm also open to alternative solutions in case I have missed something in the COM interface. If there is a solution which only works for CANoe, I can ask my boss for a license, but of course I'd prefer to do without.
there is such function, but it is restricted to be used only in test nodes
long setSignal(char signalName[], double aValue);
you can find details in:
CAPL Function Overview » Test Feature Set / Signal Access » SetSignal
Special Use Case: Signal is not known before Measurement Start
and take care about not to send for each signal a new message to avoid bus over-flooding. In my opinion it is a better style to set all signals for whole message and to send it on change only when it is not cyclic. Signal updates in cyclic messages mostly have to be sent in next cycle.

Consuming OSX mouse/trackpad events with an event tap

I'm trying to add an event trap to enable/disable event from my magic trackpad. I thought this would be straight forward, i.e. register an event trap and when required, discard the event by returning NULL. The idea is to use the pad for some specific, time consuming data entry, the applications to enter the data into are third party ones so I can't just add code to do want I want there. So i figured I'd monitor the system events and then send the desired input via a bunch of CGEventCreateKeyboardEvents.
The problem is returning null does not seem to discard the events, a bit more investigation suggests that this is not restricted to those coming from the trackpad but also my default usb mouse.
My code is below. With what is below i'd expect not to be able to move the mouse, if I change (A) to use kCGEventScrollWheel or kCGEventLeftMouseDragged then event is consumed, i.e. scrolling or left btn drag don't occur. Does this mean that not all events can be discarded? Hopefully I'm just missing something obvious here
#define case_print(a) case a: printf("%s - %d\n",#a,a); break;
CGEventRef eventOccurred(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
int subType = CGEventGetIntegerValueField(event, kCGMouseEventSubtype);
if (type == NSEventTypeGesture || subType == NX_SUBTYPE_MOUSE_TOUCH) {
printf("touchpad\n");
switch(type) {
case_print(kCGEventNull)
case_print(kCGEventLeftMouseDown)
case_print(kCGEventLeftMouseUp)
case_print(kCGEventRightMouseDown)
case_print(kCGEventRightMouseUp)
case_print(kCGEventMouseMoved)
case_print(kCGEventLeftMouseDragged)
case_print(kCGEventRightMouseDragged)
case_print(kCGEventScrollWheel)
case_print(kCGEventOtherMouseDown)
case_print(kCGEventOtherMouseUp)
case_print(kCGEventOtherMouseDragged)
case_print(kCGEventTapDisabledByTimeout)
case_print(kCGEventTapDisabledByUserInput)
case_print(NSEventTypeGesture)
case_print(NSEventTypeMagnify)
case_print(NSEventTypeSwipe)
case_print(NSEventTypeRotate)
case_print(NSEventTypeBeginGesture)
case_print(NSEventTypeEndGesture)
default:
printf("default: %d\n",type);
break;
}
event = NULL;
} else {
if (type == kCGEventMouseMoved) { // (A)
printf("discarding mouse event");
event = NULL;
}
}
return event;
}
CFMachPortRef createEventTap() {
CGEventMask eventMask = NSAnyEventMask;
if (!AXAPIEnabled() && !AXIsProcessTrusted()) {
printf("axapi not enabled");
}
return CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
eventMask,
eventOccurred,
NULL);
}
int main (int argc, const char * argv[]) {
CFMachPortRef tap = createEventTap();
if (tap) {
CFRunLoopSourceRef rl = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), rl, kCFRunLoopCommonModes);
CGEventTapEnable(tap, true);
CFRunLoopRun();
printf("Tap created.\n");
sleep(-1);
} else {
printf("failed!\n");
}
return 0;
}
Note, "axapi not enabled" is not output although i don't think the accessibility option affects anything but the keyboard events.
BTW, I've seen a few similar posts on how to get the events from the touch pad, just nothing applicable to discarding them (other than returning null should work).
I think atm it is not possible to simply discard these events. From the CGEventTypes.h header file:
"The event passed to the callback is
retained by the calling code, and is
released after the callback returns
and the data is passed back to the
event system. If a different event is
returned by the callback function,
then that event will be released by
the calling code along with the
original event, after the event data
has been passed back to the event
system."
I've played around with this a little and it seems after the callback returns the window server actively checks again for what you've done to the event. It only allows deletion of key events and mouse up/down events, but just as it ignores deletion of mouse moved events (well, the mouse rendering is processed elsewhere anyways, I guess) it seems to ignore deletion (i.e. your callback returning NULL) for mouse gestures.
Figures, considering tapping for gestures is not specifically explained in the documentation (no type defined at this level).
I tried returning a different event (a key press) and this one is then handled additionally to the original gesture. Releasing the event in your callback doesn't do it, either (of course), just results in an exception.
The only thing I didn't try is directly manipulating the passed CGEvent's internal data to at least make the gesture do nothing (deleting all movement and such), but that's kind of hard because there are no specific methods defined to do that. I'm pretty sure the needed information is somewhere in the various fields accessible via the CGEventSet* methods, though.
I looked down as far as IOLLEvent.h to figure out their data structure, but this was way too ugly for my taste to dig into much further. Let's hope Lion offers a little more regarding the gesture type of events on the CF level.
I can corroborate that on 10.6 neither of the following methods succeed for this purpose.
1-returning NULL
2-returning a new mouse event with the previous cursor position
3-returning the passed event with it's kCGMouseEventDelta's changed to 0
I can not speak of 10.7 but let's just say i would not get my hopes up.
There is one thing however that you could do instead of dropping the movement move it back to the previous location with CGWarpMouseCursorPosition, in effect the cursor will stop moving with only a slight change on the first one.
CGAssociateMouseAndMouseCursorPosition(FALSE);
Will prevent the mouse from functioning while your app is active.
Still exploring if I can extend that to global apps...
http://lists.apple.com/archives/quartz-dev/2007/May/msg00112.html
If your tap is passive, returning NULL will leave the event stream unaffected. From the CGEventTapCallBack reference documentation:
"If the event tap is an passive
listener, your callback function may
return the event that is passed in, or
NULL. In either case, the event stream
is not affected."
However, it looks like your tap is active. Thus your returning NULL should delete the event. Have you considered modifying the event to nullify the action?
Also note that the call CGEventTapCreate requires root user permissions to intercept all events. Is your process running as root?