Resetting OSC Receiver with the new port

I want the user of my app to be able to reconfigure the osc udp receiver, without terminating the app. On iOS, when the user returns from the Settings app (in which she changed the port), I want to reset the OSC Receiver in willEnterForeground method.

SInce there are no (re)-setter methods in the ReceiverUDP class, I am recreating the entire object with the new port.

void MyApp::didEnterBackground()
{
mReceiver->close();
}

void MyApp::willEnterForeground()
{
mReceiver.reset();
mReceiver = std::make_uniqueosc::ReceiverUdp(mPort);
}

This works sometimes, but most of the times I get
libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument

Anyway to work around that?

Thanks.

I’m not too familiar with iOS what happens if you destroy the whole socket, reset the receiver, before going into the background. Is there some kind of threading happening with going to the background? Also, are you using cinder’s io_service?

I don’t think it has to do with the iOS background modes. I just tried to do it in the main thread as the app was active and got the same error.

I am not using cinder’s io_service.

Thanks.

–8

Can you show me what line of receiver udp is trying to acquire the lock? Also, is the io_service you’ve instantiated threaded?

Ryan,

Here are the lines you are after, I think:

I am using the simplest non-threaded OSC Receiver setup.

Thanks.

–8

Hey eight_io,

Sorry this is so troublesome. I unfortunately can’t test on the iPhone here and I’m not getting the same error’s you’re getting. My thoughts are that this is an “order of destruction” problem, that when you close the receiver in the didEnterBackground, the io_service wants to hand back an error that the receive, or read, function, which may have already completed, has been canceled. With the willEnterForeground function, you destroy the instance before the io_service is polled, thereby taking the rug out from under the io_service.

You could check out if that’s happening by break pointing at the reset of the ReceiverUdp in willEnterForeground and at the errorHandler in the ReceiverUdp where it’s been crashing, and see which is called first. If the reset is called first then there’s the problem.

If that’s in fact what’s happening, I’d close the receiver in didEnterBackground, and then poll the io_service, so that it can propogate the error, and then reset the ReceiverUdp. Hopefully that will remove the crash. Let me know if that helps or if the problem still persists.

Best,
Ryan Bartley

Ryan,

I think I just reproduced this by making a simple change in the SimpleReceiver sample on OSX in the main thread.

Press any key and get the error I am talking about.

Thanks.

–8

@eight_io, I can confirm that i see the problem you’re talking about, with the code you posted. I’ll dig in and figure it out.

Interestingly, I pushed a key 3 times, killing the socket 3 times and put a static counter at the error point where i try and take the mutex, and it output this…

num: 0
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0
num: 1
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0
num: 2
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0
num: 3
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0
num: 4
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0
num: 5
|error  | cinder::osc::ReceiverUdp::handleError[1384] The I/O operation has been aborted because of either a thread exit or an application request., didn't receive message from 0.0.0.0

In essence it tried passing twice as many errors as it should have. Thanks for the Gist I’ll let you know when I have something.

So, it looks like the fix is a little bit more complicated and we have a big pr that I’ll merge the fix to this problem. For now, I made a comment on your gist of how to work around the problem. It is an order of destruction problem, because the io_service wants to hand you back an error telling you that you canceled the operation but you’re Receiver is already dead. Hence, the mutex not still being around. I’ll make sure to get that pr merged ASAP.

1 Like

I’ve confirmed that the problem doesn’t exist in the above pr. If you have a chance @eight_io, could you test your code against it.

@ryanbartley Ryan, your gist comment works! Thanks a lot.

This is what I did on iOS:

void AugmentedTheatreApp::willEnterForeground()
{
long newPort = [[NSUserDefaults standardUserDefaults] integerForKey:@“osc_port”];
if (mPort != newPort) {
mPort = newPort;
mReceiver.reset();
setupOSCListener(); // does mReceiver = std::make_unique<osc::ReceiverUdp>(mPort);
} else {
mReceiver->bind();
mReceiver->listen();
}
}

void AugmentedTheatreApp::didEnterBackground()
{
mReceiver->close();
io_service().poll();
}

As for PR, I don’t see its handle. Which one is it?

Thanks.

–8

I’m having a similar problem with iOS: Locking the screen of the device and going back to the app triggers a socket transport error in the UDP Receiver which completely freezes the app (just going to the home screen is fine, though).

I tried closing the receiver and re-creating it again when the error is detected but then the app crashes.

void OscTestApp::setup()
{
    mReceiver = std::make_shared<osc::ReceiverUdp>(10001);
    mReceiver->setSocketErrorFn([this](const asio::error_code &error, const asio::ip::udp::endpoint &originator)
    {
        CI_LOG_E("Code:" << error);
        CI_LOG_E("Message:" << error.message());
        CI_LOG_E("Originator:" << originator.address().to_string());
       
        mReceiver->close();
        app::App::get()->io_service().poll();
        mReceiver.reset();  // crashes anyways
        // setup();
   });

    mReceiver->bind();
    mReceiver->listen();
}

@ryanbartley’s updated OSC classes seem to fix the issue, but I’m curious if there’s any workaround for that with the current version of master?

Are you handling the receiver in the didEnterBackground method as shown above?

–8

Yes, closing and recreating the receiver on enter background/foreground works. But it’d still like to re create it when an error occurs, just in case.

@navadria, you were able to test the changes in that pr against the problem you had, and it worked? Just asking for clarification because if that’s the case I’ll try to push the merge through asap. Unfortunately, in master the hack of polling io_service, is the only way to deal with this scenario as far as I know.

@ryanbartley yes, the new api fixed this problem in a small test app, but I still haven’t had time to replace the classes in the actual project. I’ll try to integrate them and give you some feedback. The io_service hack worked for @eight_io’s case of going to background and foreground, but not from inside the error function.

What is the stack trace of the crash? I assume what’s happening is that you’re basically creating a recursive run on closing and destroying the receiver. I can count at least twice, if not more times, that you’d be closing this receiver. Your lambda would be called with an error and then you’d close the socket, which could signal another error, which would call this function again…

What I’d do in this case is create a flag that tells another mechanism to close and reset the receiver.

Fyi, the changes to OSC are merged into master that should fix many of these problems without the io_service hack.

Hey @ryanbartley, for some reason I didn’t get the notification for this.

I also thought it was a recursion problem, but the app just freezes even if you don’t do anything inside the lambda. It just calls the socket error function over and over, so it doesn’t technically crash.

Feel free to try this gist with the old OSC classes if you have some time https://gist.github.com/araid/f2240012b975478f5a6b855e36f53170