Created
August 19, 2011 22:16
-
-
Save nevali/1158167 to your computer and use it in GitHub Desktop.
NSThread+Suspendable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This should probably use a condition instead of the resume mutex alone — that way, it can sit on a pthread_cond_wait() or equivalent instead of sleeping. It might possibly be worth doing the same with the suspended mutex. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2011 Mo McRoberts. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#import <Foundation/Foundation.h> | |
/* A category on NSThread to allow threads to be suspended and resumed, | |
* provided they participate in the normal CF runloops. | |
* | |
* e.g.: | |
* | |
* [[NSThread mainThread] suspend] | |
* ... | |
* [[NSThread mainThread] resume] | |
* | |
* Key notes: | |
* 1 - Ensure the caller and callee are different threads | |
* 2 - Don't try to suspend/resume recursively or in unmatched pairs. | |
*/ | |
@interface NSThread (Suspendable) | |
- (void) suspend; | |
- (void) resume; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2011 Mo McRoberts. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#import "NSThread+Suspendable.h" | |
@interface SuspensionLock: NSObject | |
@property (assign,readonly,nonatomic) NSLock *suspended; | |
@property (assign,readonly,nonatomic) NSLock *resumed; | |
@end | |
@implementation NSThread (Suspendable) | |
- (void) suspended:(SuspensionLock *)lock | |
{ | |
/* Lock the 'suspended' mutex to signal to the suspending thread that | |
* this one is now suspended. | |
*/ | |
[lock.suspended lock]; | |
/* While we're not able to lock the 'resumed' mutex, sleep for a while */ | |
while(![lock.resumed tryLock]) | |
{ | |
usleep(250); | |
} | |
[lock.resumed unlock]; | |
/* Release the 'suspended' lock so that 'resume' can deallocate the structure */ | |
[lock.suspended unlock]; | |
} | |
- (void) suspend | |
{ | |
SuspensionLock *lock = [[SuspensionLock alloc] init]; | |
[[self threadDictionary] setValue:lock forKey:@"net.nevali.NSThread-SuspensionLock"]; | |
/* Lock the 'resumed' mutex */ | |
[lock.resumed lock]; | |
/* Perform the 'suspended:' method on the target thread */ | |
[self performSelector:@selector(suspended:) onThread:self withObject:lock waitUntilDone:NO]; | |
/* While we're still able to acquire the 'suspended' mutex, sleep */ | |
while([lock.suspended tryLock]) | |
{ | |
[lock.suspended unlock]; | |
usleep(100); | |
} | |
/* Once we can no longer grab the 'suspended' mutex, the target thread | |
* (self) is suspended; return with the 'resumed' lock held until we're | |
* ready to let go. | |
*/ | |
} | |
- (void) resume | |
{ | |
SuspensionLock *lock = [[self threadDictionary] valueForKey:@"net.nevali.NSThread-SuspensionLock"]; | |
if(lock) | |
{ | |
[[self threadDictionary] removeObjectForKey:@"net.nevali.NSThread-SuspensionLock"]; | |
/* Release the 'resumed' lock to signal the suspended thread that it | |
* should start up again | |
*/ | |
[lock.resumed unlock]; | |
/* Wait until we can get the 'suspended' lock so that we can deallocate | |
* the SuspensionLock instance. | |
*/ | |
[lock.suspended lock]; | |
[lock.suspended unlock]; | |
[lock release]; | |
} | |
} | |
@end | |
@implementation SuspensionLock | |
@synthesize suspended, resumed; | |
- (id) init | |
{ | |
if((self = [super init])) | |
{ | |
suspended = [[NSLock alloc] init]; | |
resumed = [[NSLock alloc] init]; | |
} | |
return self; | |
} | |
- (void) dealloc | |
{ | |
[suspended release]; | |
[resumed release]; | |
[super dealloc]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment