-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathRunLoopController.m
124 lines (90 loc) · 3.12 KB
/
RunLoopController.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
* Copyright Andy Duplain (c)2014.
*
* See: https://github.com/trojanfoe/RunLoopController
*
* Licensed under the MIT License.
*/
#import "RunLoopController.h"
#ifdef LOGGING
#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__)
#else
#define LOG(fmt, ...) /* nothing */
#endif
static NSString * const _threadDictKey = @"RunLoopController";
static NSInteger _instanceCount = 0;
@implementation RunLoopController
#pragma mark - RunLoopController methods
+ (RunLoopController *)currentRunLoopController {
return [[[NSThread currentThread] threadDictionary] objectForKey:_threadDictKey];
}
+ (RunLoopController *)mainRunLoopController {
return [[[NSThread mainThread] threadDictionary] objectForKey:_threadDictKey];
}
+ (NSInteger)instanceCount {
return _instanceCount;
}
- (void)register {
NSAssert(![RunLoopController currentRunLoopController], @"Already registered");
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
_terminatePort = [NSMachPort new];
_terminatePort.delegate = self;
[runloop addPort:_terminatePort
forMode:NSDefaultRunLoopMode];
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
[threadDict setObject:self forKey:_threadDictKey];
_instanceCount++;
LOG(@"%p: register. instanceCount=%ld", self, (long)_instanceCount);
}
- (void)deregister {
NSAssert([RunLoopController currentRunLoopController], @"Not registered");
_instanceCount--;
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
[threadDict removeObjectForKey:_threadDictKey];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop removePort:_terminatePort
forMode:NSDefaultRunLoopMode];
[_terminatePort invalidate];
_terminatePort = nil;
LOG(@"%p: deregister. instanceCount=%ld", self, (long)_instanceCount);
}
- (BOOL)run {
return [self runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
- (BOOL)runMode:(NSString *)mode
beforeDate:(NSDate *)limitDate {
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
if (![runloop runMode:mode
beforeDate:limitDate]) {
LOG(@"%p: Error in [NSRunLoop runMode:beforeDate:]", self);
return NO;
}
return !_terminate;
}
- (void)terminate {
LOG(@"%p: Terminating", self);
_terminate = YES;
[self signal];
// If we are not the main thread, then find the RunLoopController for the main thread
// and also signal it's mach port to wake up its run loop
if (![NSThread isMainThread]) {
LOG(@"%p: Signalling main thread's run loop controller", self);
RunLoopController *runLoopController = [RunLoopController mainRunLoopController];
[runLoopController signal];
}
}
- (BOOL)shouldTerminate {
return _terminate;
}
- (void)signal {
[_terminatePort sendBeforeDate:[NSDate date]
components:nil
from:nil
reserved:0];
}
#pragma mark - NSMachPortDelegate methods
- (void)handleMachMessage:(void *)machMessage {
LOG(@"%p: Mach message received", self);
}
@end