OpenClonk
StdSchedulerMac.mm
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4Include.h"
17 #include "platform/StdScheduler.h"
18 #import <Cocoa/Cocoa.h>
19 
20 using namespace std;
21 
22 @class SCHAdditions;
23 
24 @interface SCHAddition : NSObject
25 {
26 @protected
29 }
30 - (id) initWithProc:(StdSchedulerProc*)_proc;
31 - (void) registerAt:(SCHAdditions*) _additions;
32 - (void) unregisterFrom:(SCHAdditions*) _additions;
33 - (bool) shouldExecuteProc;
34 - (void) changed;
35 @end
36 
37 @interface SCHNotify : SCHAddition
38 {
39  list<pair<CFRunLoopSourceRef, CFSocketRef>> socketSources;
40 }
41 - (void) registerAt:(SCHAdditions*) _additions;
42 - (void) unregisterFrom:(SCHAdditions*) _additions;
43 @end
44 
45 @interface SCHTimer : SCHAddition
46 {
47 @private
48  NSTimer* timer;
49 }
50 - (void) registerAt:(SCHAdditions*) _additions;
51 - (void) unregisterFrom:(SCHAdditions*) _additions;
52 @end
53 
54 @interface SCHAdditions : NSObject
55 {
56  NSMutableDictionary* procAdditions;
57 }
58 + (SCHAdditions*) requestAdditionsForScheduler:(StdScheduler*) scheduler;
59 - (id) initWithScheduler:(StdScheduler*) scheduler;
60 - (SCHAddition*) additionForProc:(StdSchedulerProc*) proc;
61 - (SCHAddition*) assignAdditionForProc:(StdSchedulerProc*) proc;
62 - (void) changed:(SCHAddition*)addition;
63 - (BOOL) removeAdditionForProc:(StdSchedulerProc*) proc;
64 - (void) start;
65 @property(readonly) __weak NSRunLoop* runLoop;
66 @property(readonly) StdScheduler* scheduler;
67 @end
68 
69 @implementation SCHAdditions
70 
71 static NSMutableDictionary* additionsDictionary;
72 
73 - (int) numberOfAdditions { return [additionsDictionary count]; }
74 
75 - (id) initWithScheduler:(StdScheduler*) scheduler
76 {
77  if (self = [super init])
78  {
79  _scheduler = scheduler;
80  procAdditions = [NSMutableDictionary new];
81  return self;
82  } else
83  return nil;
84 }
85 
86 - (SCHAddition*) additionForProc:(StdSchedulerProc*) proc
87 {
88  return [procAdditions objectForKey:[NSNumber valueWithPointer:proc]];
89 }
90 
91 - (BOOL) removeAdditionForProc:(StdSchedulerProc*) proc
92 {
93  auto key = [NSNumber valueWithPointer:proc];
94  SCHAddition* x = [procAdditions objectForKey:key];
95  if (x)
96  {
97  [x unregisterFrom:self];
98  [procAdditions removeObjectForKey:key];
99  return YES;
100  }
101  else
102  return NO;
103 }
104 
105 - (SCHAddition*) assignAdditionForProc:(StdSchedulerProc*) proc
106 {
107  auto timerInterval = proc->TimerInterval();
108  auto addition =
109  timerInterval ? [[SCHTimer alloc] initWithProc:proc] :
110  proc->IsNotify() ? [[SCHNotify alloc] initWithProc:proc] :
111  nullptr;
112  if (addition)
113  {
114  [procAdditions setObject:addition forKey:[NSNumber valueWithPointer:proc]];
115  if (_runLoop)
116  [addition registerAt:self];
117  return addition;
118  } else
119  return nullptr;
120 }
121 
122 - (void) start
123 {
124  auto current = [NSRunLoop currentRunLoop];
125  if (current != [NSRunLoop mainRunLoop])
126  return; // oh well
127  _runLoop = current;
128  [procAdditions enumerateKeysAndObjectsUsingBlock:
129  ^void (id key, SCHAddition* obj, BOOL* stop) { [obj registerAt:self]; }];
130 }
131 
132 - (void) changed:(SCHAddition*)addition
133 {
134  [addition unregisterFrom:self];
135  if (_runLoop)
136  [addition registerAt:self];
137 }
138 
139 + (void) removeAdditions:(SCHAdditions*)additions
140 {
141  auto key = [NSNumber valueWithPointer:additions.scheduler];
142  @synchronized (additionsDictionary)
143  {
144  [additionsDictionary removeObjectForKey:key];
145  }
146 }
147 
148 + (SCHAdditions*) requestAdditionsForScheduler:(StdScheduler *)scheduler
149 {
150  static dispatch_once_t onceToken;
151  dispatch_once(&onceToken,
152  ^{ additionsDictionary = [NSMutableDictionary new]; });
153  auto key = [NSNumber valueWithPointer:scheduler];
154  @synchronized (additionsDictionary)
155  {
156  SCHAdditions* additions = [additionsDictionary objectForKey:key];
157  if (!additions)
158  {
159  additions = [[SCHAdditions alloc] initWithScheduler:scheduler];
160  [additionsDictionary setObject:additions forKey:key];
161  }
162  return additions;
163  }
164 }
165 
166 @end
167 
168 @implementation SCHAddition
169 - (id) initWithProc:(StdSchedulerProc *) _proc
170 {
171  if (self = [super init])
172  {
173  proc = _proc;
174  return self;
175  } else
176  return nil;
177 }
178 - (bool) shouldExecuteProc
179 {
180  if (!proc)
181  return false;
182  auto s = schedulerAdditions;
183  return s && !s.scheduler->IsInManualLoop();
184 }
185 - (void) registerAt:(SCHAdditions*) _additions
186 {
187  schedulerAdditions = _additions;
188 }
189 - (void) unregisterFrom:(SCHAdditions*) _additions
190 {
191  schedulerAdditions = nil;
192 }
193 - (void) changed
194 {
195  [schedulerAdditions changed:self];
196 }
197 @end
198 
199 @implementation SCHTimer
200 - (id) initWithProc:(StdSchedulerProc *) _proc
201 {
202  if (self = [super init])
203  {
204  proc = _proc;
205  return self;
206  } else
207  return nil;
208 }
209 - (void) run:(id) sender
210 {
211  auto i = timer;
212  if (i && [self shouldExecuteProc])
213  proc->Execute();
214 }
215 - (void) registerAt:(SCHAdditions*) _additions
216 {
217  [super registerAt:_additions];
218  auto loop = _additions.runLoop;
219  timer = [NSTimer timerWithTimeInterval:proc->TimerInterval()/1000.0 target:self selector:@selector(run:) userInfo:nil repeats:YES];
220  [loop addTimer:timer forMode:NSDefaultRunLoopMode];
221 }
222 - (void) unregisterFrom:(SCHAdditions*) _additions
223 {
224  [timer invalidate];
225  timer = nil;
226  [super unregisterFrom:_additions];
227 }
228 @end
229 
230 @implementation SCHNotify
231 void callback (CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
232 {
233  auto notify = (__bridge SCHNotify*)info;
234  pollfd p = {.fd=CFSocketGetNative(s)};
235  if ([notify shouldExecuteProc])
236  notify->proc->Execute(-1, &p);
237 }
238 - (void) registerAt:(SCHAdditions*) _additions
239 {
240  [super registerAt:_additions];
241  vector<struct pollfd> vecs;
242  proc->GetFDs(vecs);
243  CFSocketContext ctx =
244  {
245  .info = (__bridge void*)self,
246  .retain = CFRetain,
247  .release = CFRelease
248  };
249  for (auto p : vecs)
250  {
251  auto socket = CFSocketCreateWithNative(NULL,
252  p.fd, kCFSocketReadCallBack,
253  callback, &ctx
254  );
255  auto runLoopSource = CFSocketCreateRunLoopSource(NULL, socket, 0);
256  CFRunLoopAddSource([_additions.runLoop getCFRunLoop], runLoopSource, kCFRunLoopDefaultMode);
257  socketSources.push_back(make_pair(runLoopSource, socket));
258  }
259 }
260 - (void) unregisterFrom:(SCHAdditions*) _additions
261 {
262  auto runLoop = [_additions.runLoop getCFRunLoop];
263  for (auto r : socketSources)
264  {
265  CFRunLoopRemoveSource(runLoop, r.first, kCFRunLoopDefaultMode);
266  CFSocketDisableCallBacks(r.second, kCFSocketReadCallBack|kCFSocketWriteCallBack);
267  CFRunLoopSourceInvalidate(r.first);
268  CFRelease(r.second);
269  CFRelease(r.first);
270  }
271  socketSources.clear();
272  [super unregisterFrom:_additions];
273 }
274 @end
275 
277 {
279 }
280 
282 {
284 }
285 
287 {
289  [x removeAdditionForProc:pProc];
290  if ([x numberOfAdditions] == 0)
291  [SCHAdditions removeAdditions:x];
292 }
293 
295 {
297 }
#define s
void Added(StdSchedulerProc *pProc)
void StartOnCurrentThread()
void Removing(StdSchedulerProc *pProc)
void Changed(StdSchedulerProc *pProc)
virtual uint32_t TimerInterval()
Definition: StdScheduler.h:84
virtual bool IsNotify()
Definition: StdScheduler.h:83
virtual void GetFDs(std::vector< struct pollfd > &)
Definition: StdScheduler.h:72
virtual bool Execute(int iTimeout=-1, pollfd *readyfds=nullptr)=0
void unregisterFrom:(SCHAdditions *_additions)
__weak SCHAdditions * schedulerAdditions
bool shouldExecuteProc()
StdSchedulerProc * proc
void registerAt:(SCHAdditions *_additions)
SCHAddition * additionForProc:(StdSchedulerProc *proc)
StdScheduler * scheduler
__weak NSRunLoop * runLoop
SCHAddition * assignAdditionForProc:(StdSchedulerProc *proc)
NSMutableDictionary * procAdditions
SCHAdditions * requestAdditionsForScheduler:(StdScheduler *scheduler)
list< pair< CFRunLoopSourceRef, CFSocketRef > > socketSources