Search code examples
c++socketsudposc

C++ Interrupting UDP Listener. Compiled using oscpack in Xcode


I have successfully incorporated an UDPreceive function into my application. HOWEVER! I can not figure out how to stop UDP listener from running infinitely. The OSCPack library has a Break() and AsynchronousBreak() built into it, but I have been unable to implement these.

in the udpSocket.cpp file within oscpack:

void Run() //the listener function (WORKING!)
{
    break_ = false; 
    //UDP Listener Code

    void Break()
    {
        break_ = true;
    }
    void AsynchronousBreak()
    {
        break_ = true;
        // Send a termination message to the asynchronous break pipe, so select() will return
        write( breakPipe_[1], "!", 1 );
    }
}

My attempt to call Break() from the packet Listener class doesn't appear to do anything, despite the compiler suggesting that everything is being called correctly:

SocketReceiveMultiplexer s;
s.Break();

Another method that I have tried was to raise an interrupt flag in accordance with the RunUntilSigInt() function. Within the packet listener class:

raise(SIGINT);

but this terminates the whole program, rather than just breaking from the UDPListener. For reference, here is the RunUntilSigInt() code within udpSocket.cpp:

void SocketReceiveMultiplexer::RunUntilSigInt()
{
    assert( multiplexerInstanceToAbortWithSigInt_ == 0 ); /* at present we support only one multiplexer instance running until sig int */
    multiplexerInstanceToAbortWithSigInt_ = this;
    signal( SIGINT, InterruptSignalHandler );
    impl_->Run();
    signal( SIGINT, SIG_DFL );
    multiplexerInstanceToAbortWithSigInt_ = 0;
}

I'm completely stuck on this one, any help/advice will be greatly appreciated.

Thanks, Tom


Solution

  • I know this is a somewhat old question, but I had to overcome this recently and didn't find a good answer online. The model used by oscpack seems to be that they control the infinite Run loop, and you implement everything you want to do inside a class derived from OscPacketListener. If you don't want to do things that way, you need to run the Run loop in a separate thread. It seems in the oscpack 1.1.0 release, there is no internal support for threading anymore. They explain in the CHANGES file for that release that you would need to implement your own threading solution. The Run routine in SocketReceiveMultiplexer never returns, so any code after that call is unreachable. The various Break routines are for controlling the execution of the Run loop from a different thread. In the below example I'm using c++11 <threads> but you can use any threading library you choose to accomplish something similar. In my example, you'll have to

    #include <threads>
    #include <mutex>
    

    and compile your code with a c++11 compatible compiler. In g++ you would need the -std=c++11 command line argument.

    If you start with the receiver example (parsing single messages example) in the SVN, you could change the main() function to be something like

    void ListnerThread()
    {
        PacketListener listener;
        UdpListeningReceiveSocket s(
                IpEndpointName( IpEndpointName::ANY_ADDRESS, PORT ),
                &listener );
        s.Run();
    }
    

    Somewhere else in your code, make a call like

    std::thread lt(ListnerThread);
    

    in order to start the listener running. You'll have to create some means of sharing information between your main thread and the listener thread. One simple method is to use a global variable surrounded by a mutex (also global). There are certainly other (better?) ways but this is very easy. Declare these globally (following their example) instead of within the ProcessMessage function:

    std::mutex oscMutex;
    
    bool a1;
    osc::int32 a2;
    float a3;
    const char *a4;
    

    Inside the ExamplePacketListener, where they set the variables from the args stream and then make a call to cout you would do something like

    oscMutex.lock();
    args >> a1 >> a2 >> a3 >> a4 >> osc::EndMessage;
    oscMutex.unlock();
    

    Just be sure to also lock() and unlock() the mutex in the same way wherever you access those variables elsewhere in your code.