zoul
zoul

Reputation: 104145

Starting and stopping operations in a thread-safe manner

I have a simple class that looks a bit like this:

@protocol Recorder
@property(BOOL) isRunning;
- (void) start;
- (void) stop;
@end

And the method implementations:

- (void) start {
    if (running)
        return;
    …
    running = YES;
}

- (void) stop {
    if (!running)
        return;
    …
    running = NO;
}

And I started thinking about thread safety. The current solution is not thread safe, right? How about this:

- (void) start {
    @synchronized(self) {
        if (running)
            return;
        …
        running = YES;
    }
}

Is this correct, provided that the -stop method is also synchronized? I don’t like the extra nesting introduced by @synchronized, though. Would explicit lock work?

- (void) stop {
    [startStopLock lock];
    if (running)
        return;
    …
    running = YES;
    [startStopLock unlock];
}

Or could I do even this?

enum { Running, Stopped };
NSConditionLock *startStopLock;

- (void) start {
    if (![startStopLock tryLockWithCondition:Stopped])
         return;
    …
    [startStopLock unlockWithCondition:Running];
}

Is this solution correct? Would you do things differently?

Upvotes: 3

Views: 581

Answers (1)

Dave Dunn
Dave Dunn

Reputation: 119

What language is this? You are correct, the first version is not thread safe.

The synchronized version is thread safe. With an explicit lock you need to be careful not to miss the unlock on the early return path.

If you have access to locked xchg intrinsics, you can do it fairly easily with an atomic exchange operation. cmpxchg also works.

start() {
    if (locked_xchg(running, YES) == YES) {
        // the old value was YES, so nothing to do
        return
    }

Upvotes: 1

Related Questions