4

Right now I'm working on a program using IOBluetooth and I need to have synchronous reads, i.e. I call a method, it writes a given number of bytes to the port, then reads a given number and returns them. I currently have a complex system of NSThreads, NSLocks, and NSConditions that, while it sort of works, is very slow. Also, after certain calls, I need to make sure there's no extra data, so I'd normally flush the buffer, but with IOBluetooth's asynchronous callback that's not possible - any thoughts on how to make sure that no matter what, all data received after a specific point is data that's received after that point?

I really haven't dealt at all with synchronization and multithreading of this type, since all the work I've done so far is using synchronous calls, so I'd appreciate any thoughts on the matter.

Here's the callback for incoming data (the "incomingData" object is NSMutableData):

- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength {
    [dataLock lock];
    NSData *data = [NSData dataWithBytes:dataPointer length:dataLength];
    [incomingData appendData:data];

    if (dataWaitCondition && [incomingData length] >= bytesToWaitFor) {
        [dataWaitCondition signal];
    }

    [dataLock unlock];
    [delegate bluetoothDataReceived];
}

And here's the method that waits until the given number of bytes has been received before returning the data object (this gets called from an alternate thread).

- (NSData *)waitForBytes:(int)numberOfBytes {

    bytesToWaitFor = numberOfBytes;
    [dataLock lock];
    dataWaitCondition = [[NSCondition alloc] init];
    [dataWaitCondition lock];
    [dataLock unlock];
    while ([incomingData length] < numberOfBytes) {
        [dataWaitCondition wait];
    }
    [dataLock lock];
    NSData *data = [incomingData copy];
    [dataWaitCondition unlock];
    dataWaitCondition = NULL;
    [dataLock unlock];

    return data;
}
jstm88
  • 3,335
  • 4
  • 38
  • 55

1 Answers1

0

Doing any kind of IO/communication in a synchronous way will get you into trouble.

You can avoid multi threading and locks by using a simple state machine for your application logic. Whenever data is received, the state machine is triggered and can process the data. If all data is there, you can do the next step in your app. You can use synchronous calls for sending if you like as it will just drop the data with the Bluetooth system.

mringwal
  • 476
  • 3
  • 10
  • I actually solved parts of this, and yes, you're absolutely right, but the bigger problem that came up was the fact that async calls with new data come in at far too slow a rate. The application is realtime but we only receive incoming data from IOBluetooth at about 1-5 Hz, which is far too slow. Any idea why data comes in so slowly? I need to have the calls come in as fast as possible. – jstm88 Oct 03 '11 at 15:57
  • RFCOMM simulates a serial port, but has to send L2CAP data packets of that serial data. Because of this, there might be some queueing on the sending side, maybe that can be made to send more often. You can also check with the PacketLogger to see how fast your data is coming in, and whether it is buffered on the OS X side. – mringwal Oct 17 '11 at 08:56