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;
}
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.