openclonk/src/platform/StdSchedulerMac.mm

298 lines
7.1 KiB
Plaintext

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#include "C4Include.h"
#include "platform/StdScheduler.h"
#import <Cocoa/Cocoa.h>
using namespace std;
@class SCHAdditions;
@interface SCHAddition : NSObject
{
@protected
__weak SCHAdditions* schedulerAdditions;
StdSchedulerProc* proc;
}
- (id) initWithProc:(StdSchedulerProc*)_proc;
- (void) registerAt:(SCHAdditions*) _additions;
- (void) unregisterFrom:(SCHAdditions*) _additions;
- (bool) shouldExecuteProc;
- (void) changed;
@end
@interface SCHNotify : SCHAddition
{
list<pair<CFRunLoopSourceRef, CFSocketRef>> socketSources;
}
- (void) registerAt:(SCHAdditions*) _additions;
- (void) unregisterFrom:(SCHAdditions*) _additions;
@end
@interface SCHTimer : SCHAddition
{
@private
NSTimer* timer;
}
- (void) registerAt:(SCHAdditions*) _additions;
- (void) unregisterFrom:(SCHAdditions*) _additions;
@end
@interface SCHAdditions : NSObject
{
NSMutableDictionary* procAdditions;
}
+ (SCHAdditions*) requestAdditionsForScheduler:(StdScheduler*) scheduler;
- (id) initWithScheduler:(StdScheduler*) scheduler;
- (SCHAddition*) additionForProc:(StdSchedulerProc*) proc;
- (SCHAddition*) assignAdditionForProc:(StdSchedulerProc*) proc;
- (void) changed:(SCHAddition*)addition;
- (BOOL) removeAdditionForProc:(StdSchedulerProc*) proc;
- (void) start;
@property(readonly) __weak NSRunLoop* runLoop;
@property(readonly) StdScheduler* scheduler;
@end
@implementation SCHAdditions
static NSMutableDictionary* additionsDictionary;
- (int) numberOfAdditions { return [additionsDictionary count]; }
- (id) initWithScheduler:(StdScheduler*) scheduler
{
if (self = [super init])
{
_scheduler = scheduler;
procAdditions = [NSMutableDictionary new];
return self;
} else
return nil;
}
- (SCHAddition*) additionForProc:(StdSchedulerProc*) proc
{
return [procAdditions objectForKey:[NSNumber valueWithPointer:proc]];
}
- (BOOL) removeAdditionForProc:(StdSchedulerProc*) proc
{
auto key = [NSNumber valueWithPointer:proc];
SCHAddition* x = [procAdditions objectForKey:key];
if (x)
{
[x unregisterFrom:self];
[procAdditions removeObjectForKey:key];
return YES;
}
else
return NO;
}
- (SCHAddition*) assignAdditionForProc:(StdSchedulerProc*) proc
{
auto timerInterval = proc->TimerInterval();
auto addition =
timerInterval ? [[SCHTimer alloc] initWithProc:proc] :
proc->IsNotify() ? [[SCHNotify alloc] initWithProc:proc] :
nullptr;
if (addition)
{
[procAdditions setObject:addition forKey:[NSNumber valueWithPointer:proc]];
if (_runLoop)
[addition registerAt:self];
return addition;
} else
return nullptr;
}
- (void) start
{
auto current = [NSRunLoop currentRunLoop];
if (current != [NSRunLoop mainRunLoop])
return; // oh well
_runLoop = current;
[procAdditions enumerateKeysAndObjectsUsingBlock:
^void (id key, SCHAddition* obj, BOOL* stop) { [obj registerAt:self]; }];
}
- (void) changed:(SCHAddition*)addition
{
[addition unregisterFrom:self];
if (_runLoop)
[addition registerAt:self];
}
+ (void) removeAdditions:(SCHAdditions*)additions
{
auto key = [NSNumber valueWithPointer:additions.scheduler];
@synchronized (additionsDictionary)
{
[additionsDictionary removeObjectForKey:key];
}
}
+ (SCHAdditions*) requestAdditionsForScheduler:(StdScheduler *)scheduler
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{ additionsDictionary = [NSMutableDictionary new]; });
auto key = [NSNumber valueWithPointer:scheduler];
@synchronized (additionsDictionary)
{
SCHAdditions* additions = [additionsDictionary objectForKey:key];
if (!additions)
{
additions = [[SCHAdditions alloc] initWithScheduler:scheduler];
[additionsDictionary setObject:additions forKey:key];
}
return additions;
}
}
@end
@implementation SCHAddition
- (id) initWithProc:(StdSchedulerProc *) _proc
{
if (self = [super init])
{
proc = _proc;
return self;
} else
return nil;
}
- (bool) shouldExecuteProc
{
if (!proc)
return false;
auto s = schedulerAdditions;
return s && !s.scheduler->IsInManualLoop();
}
- (void) registerAt:(SCHAdditions*) _additions
{
schedulerAdditions = _additions;
}
- (void) unregisterFrom:(SCHAdditions*) _additions
{
schedulerAdditions = nil;
}
- (void) changed
{
[schedulerAdditions changed:self];
}
@end
@implementation SCHTimer
- (id) initWithProc:(StdSchedulerProc *) _proc
{
if (self = [super init])
{
proc = _proc;
return self;
} else
return nil;
}
- (void) run:(id) sender
{
auto i = timer;
if (i && [self shouldExecuteProc])
proc->Execute();
}
- (void) registerAt:(SCHAdditions*) _additions
{
[super registerAt:_additions];
auto loop = _additions.runLoop;
timer = [NSTimer timerWithTimeInterval:proc->TimerInterval()/1000.0 target:self selector:@selector(run:) userInfo:nil repeats:YES];
[loop addTimer:timer forMode:NSDefaultRunLoopMode];
}
- (void) unregisterFrom:(SCHAdditions*) _additions
{
[timer invalidate];
timer = nil;
[super unregisterFrom:_additions];
}
@end
@implementation SCHNotify
void callback (CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
auto notify = (__bridge SCHNotify*)info;
pollfd p = {.fd=CFSocketGetNative(s)};
if ([notify shouldExecuteProc])
notify->proc->Execute(-1, &p);
}
- (void) registerAt:(SCHAdditions*) _additions
{
[super registerAt:_additions];
vector<struct pollfd> vecs;
proc->GetFDs(vecs);
CFSocketContext ctx =
{
.info = (__bridge void*)self,
.retain = CFRetain,
.release = CFRelease
};
for (auto p : vecs)
{
auto socket = CFSocketCreateWithNative(NULL,
p.fd, kCFSocketReadCallBack,
callback, &ctx
);
auto runLoopSource = CFSocketCreateRunLoopSource(NULL, socket, 0);
CFRunLoopAddSource([_additions.runLoop getCFRunLoop], runLoopSource, kCFRunLoopDefaultMode);
socketSources.push_back(make_pair(runLoopSource, socket));
}
}
- (void) unregisterFrom:(SCHAdditions*) _additions
{
auto runLoop = [_additions.runLoop getCFRunLoop];
for (auto r : socketSources)
{
CFRunLoopRemoveSource(runLoop, r.first, kCFRunLoopDefaultMode);
CFSocketDisableCallBacks(r.second, kCFSocketReadCallBack|kCFSocketWriteCallBack);
CFRunLoopSourceInvalidate(r.first);
CFRelease(r.second);
CFRelease(r.first);
}
socketSources.clear();
[super unregisterFrom:_additions];
}
@end
void StdScheduler::StartOnCurrentThread()
{
[[SCHAdditions requestAdditionsForScheduler:this] start];
}
void StdScheduler::Added(StdSchedulerProc *pProc)
{
[[SCHAdditions requestAdditionsForScheduler:this] assignAdditionForProc:pProc];
}
void StdScheduler::Removing(StdSchedulerProc *pProc)
{
auto x = [SCHAdditions requestAdditionsForScheduler:this];
[x removeAdditionForProc:pProc];
if ([x numberOfAdditions] == 0)
[SCHAdditions removeAdditions:x];
}
void StdScheduler::Changed(StdSchedulerProc* pProc)
{
[[[SCHAdditions requestAdditionsForScheduler:this] additionForProc:pProc] changed];
}