Skip to content

Instantly share code, notes, and snippets.

@nevali
Created August 19, 2011 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nevali/1158167 to your computer and use it in GitHub Desktop.
Save nevali/1158167 to your computer and use it in GitHub Desktop.
NSThread+Suspendable
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.
/*
* 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
/*
* 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