-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYThreadPauser.m
96 lines (74 loc) · 3.02 KB
/
SSYThreadPauser.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#import "SSYThreadPauser.h"
#import "NSInvocation+Quick.h"
NSString* const SSYThreadPauserKeyLock = @"SSYThreadPauserKeyLock" ;
NSString* const SSYThreadPauserKeyInvocation = @"SSYThreadPauserKeyInvocation" ;
#define WORK_IS_NOT_DONE 0
#define WORK_IS_DONE 1
@implementation SSYThreadPauser
- (void)beginWorkWithInfo:(NSDictionary*)info {
#if !__has_feature(objc_arc)
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
#endif
NSConditionLock* lock = [info objectForKey:SSYThreadPauserKeyLock] ;
[lock lock] ;
NSInvocation* invocation = [info objectForKey:SSYThreadPauserKeyInvocation] ;
// Do actual work
[invocation invoke] ;
[lock unlockWithCondition:WORK_IS_DONE] ;
#if !__has_feature(objc_arc)
[pool drain] ;
#endif
}
+ (BOOL)blockUntilWorker:(id)worker
selector:(SEL)selector
object:(id)object
timeout:(NSTimeInterval)timeout {
/*
Here's a fun 64-bit quirk that set me back a couple hours. If you ever
write a method which creates a date from an NSTimeInterval parameter, make
sure that you can't create an unreasonably large date. I'd been passing
FLT_MAX to indicate "no timeout" to such a method.
Works OK in 32-bit. In 64-bit, -[NSDate compare:] and -[NSDate laterDate:]
still work as expected. But other methods may not. For example,
-[NSConditionLock lockWhenCondition:beforeDate:] seems to think that such
a "float max date" has already past and returns NO immediately, regardless
of its 'condition'.
The solution is the next two lines…
*/
NSTimeInterval maxTimeout = [[NSDate distantFuture] timeIntervalSinceNow] ;
timeout = MIN(timeout, maxTimeout) ;
NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout] ;
SSYThreadPauser* instance = [[SSYThreadPauser alloc] init] ;
NSInvocation* invocation = [NSInvocation invocationWithTarget:worker
selector:selector
retainArguments:YES
argumentAddresses:&object] ;
NSConditionLock* lock = [[NSConditionLock alloc] initWithCondition:WORK_IS_NOT_DONE] ;
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
invocation, SSYThreadPauserKeyInvocation,
lock, SSYThreadPauserKeyLock,
nil] ;
// Begin Work
NSThread* workerThread = [[NSThread alloc] initWithTarget:instance
selector:@selector(beginWorkWithInfo:)
object:info] ;
#if !__has_feature(objc_arc)
[workerThread autorelease] ;
#endif
// Name the thread, to help in debugging.
[workerThread setName:@"Worker created by SSYThreadPauser"] ;
[workerThread start] ;
// Will block here until work is done, or timeout
BOOL workFinishedInTime = [lock lockWhenCondition:WORK_IS_DONE
beforeDate:timeoutDate] ;
if (workFinishedInTime) {
[lock unlock] ;
}
[workerThread cancel] ;
#if !__has_feature(objc_arc)
[instance release] ;
[lock release] ;
#endif
return (workFinishedInTime) ;
}
@end