Search code examples
c#macosunity-game-engineudpclientosc

Why do I stop receiving OSC messages after a while on Mac?


I'm working on a Unity game that receives OSC messages from the Muse EEG headset. I've tried two 3rd party C# libraries to handle the OSC communication, UnityOSC and unity-OSC-receiver. Both implement the OSC communication with an underlying System.Net.Sockets.UdpClient. Everything is running smoothly on Windows, but on OSX, after a while, I just stop receiving messages every time. No exceptions or error messages, no indication of what went wrong at all, just silence.

My application roughly works as follows:

  • Start a thread that spawns a process that runs Muse-IO. This makes the headset start sending messages. After starting the process, this thread is just chilling on process.WaitforExit()
  • Another thread runs a while loop - not in MonoBehavior.Update(), that's not fast enough - that keeps receiving and processing OSC messages. In both libraries, this essentially boils down to calling UdpClient.Receive()
  • Game uses the processed messages in the normal Unity update cycle.

Some 120 to 140 seconds after the connection is initialized, the stream of messages just stops, and so far I haven't been able to figure out why. The connection indicator light on the headset stays on, but nothing indicates it's actually still sending data.

Things I've ruled out:

  • It's not because the number of messages or the size of the messages. If I modify the command to the headset to only send some categories of messages, cutting the total in half (from about 600/s to 300/s), the timeout still happens at the same time.
  • It's not the OSC library. I get the exact same results with both OSC libraries.
  • It's not the firewall. The firewall is off.
  • It's probably not the port being used by something else. I tried different ports with the same result.
  • It doesn't appear to be Muse's OSX driver. When I use their GUI to visualize the incoming data, it keeps receiving data for as long as I want.

I suspect that Mono, Unity or OSX might be shutting down (garbage-collecting?) the Muse-IO process or thread, because the time before the problem occurs seems to be pretty much constant regardless of what I try. But I'm unsure how to further diagnose, let alone fix this now. Any clues, suggestions or amazing solutions would be most welcome.


Solution

  • I found the cause.

    After spawning the I/O process, the thread would do

    print("Process started!");
    process.PriorityClass = ProcessPriorityClass.High;
    process.WaitforExit();
    

    In hindsight, that print statement is really poorly placed, oh well. It worked fine on Windows. Changing process priority only requires admin privilege if you're increasing it to Realtime, according to the docs. Not so on Mac though. apparently setting it to High also requires elevated rights on OSX. The resulting exception was silent/undetected/uncaught because it happens outside the main thread.

    Then, several minutes later, it seems the thread is garbage collected, including its child process, even though that's still running. That delay really threw me off, making me look for the cause in all the wrong places.

    Lessons learned:

    • Be more careful with possible exceptions when multithreading,
    • Don't mess with process priority if you don't absolutely have to,
    • And never trust the docs.