I have a simple scenario where a NSTimer
is scheduled on a background thread to send a packet through a socket every so often. My main thread however, is responsible for destroying the timer and the socket.
The destruction looks like this:
if (self.connected) {
[self.pingTimer invalidate];
if (self.socket != -1) {
close(self.socket);
self.socket = -1;
}
self.connected = NO;
}
How can I ensure in Objective-C that once this section of code is entered, the scheduler isn't allowed to suspend this thread until the code section is over? In other words I want to make this code section atomic.
My understanding is that the @synchronized
directive is a high-level semaphore/mutex so to ensure atomicity of that code block, I would need to enclose the code block itself, as well as all the code which references either self.connected
, self.pingTimer
and self.socket
in @synchronized
directives. Am I correct in thinking this?
@synchronized
is one way. Another would be to use NSLock
or NSRecursiveLock
. You can even use plain POSIX pthread locking primitives. See the Multithreading Guide.
@interface Stuff
{
NSLock* lock;
int cell;
}
- (int) cellValue;
- (void) someOperation;
@end
@implementation Stuff
- (id) init
{
if (self = [super init])
{
lock = [[NSLock alloc] init];
cell = 0;
}
return self;
}
- (int) cellValue
{
[lock lock];
@try
{
return cell;
}
@finally
{
[lock unlock];
}
}
- (void) someOperation
{
[lock lock];
@try
{
// Do something involving access to stuff protected by
// the lock (only cell here, but could be more, of course)
}
@finally
{
[lock unlock];
}
}
@end
Using @synchronized
, the code above would look simpler:
- (int) cellValue
{
@synchronized (self)
{
return cell;
}
}
As far as I know there is no way to prevent the OS from preempting your thread and have another run in its place. The best you can do is synchronize the access. But in reading your code I infer the following:
I think its best to avoid synchronization whenever possible. Fortunately, you are using a NSTimer, which will make things trivial.
NSTimer does NOT run on a background thread but on a NSRunLoop and probably on the main run loop just like your dealloc
. Timers are event sources for the run loop. When they fire they enqueue an event, which in turn calls your code when it gets processed. Because run loops process one event at a time it means that the code in your scheduled timer and the code in your dealloc get fully executed before the other can run. It also means that you don't need any special synchronization mechanism.
- (void) dealloc
{
// ...
if (_connected) {
[_pingTimer invalidate];
if (_socket != -1)
close(_socket);
}
// ...
[super dealloc];
}
Note: I prefer to use the state variables directly when I can unless the setters have other side effects. There is also no point in setting state variables in dealloc simply because the object will disapear once the method call is done.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With