Implement a CFRunLoop based event loop.

This enables efficient integration of the kqueue based I/O processing with Apple OS based UI apps.

On top of that, an FSEvent based directory watcher can now be implemented to replace the inefficient generic watcher that is used on macOS right now.
This commit is contained in:
Sönke Ludwig 2020-05-19 10:42:25 +02:00
parent 4b1afa8d6c
commit e28450f9f5
4 changed files with 109 additions and 1 deletions

View file

@ -23,6 +23,12 @@ configuration "epoll-gaia" {
versions "EventcoreEpollDriver"
}
configuration "cfrunloop" {
platforms "osx"
versions "EventcoreCFRunLoopDriver"
lflags "-framework" "CoreFoundation"
}
configuration "kqueue" {
platforms "osx" "freebsd"
versions "EventcoreKqueueDriver"

View file

@ -2,14 +2,16 @@ module eventcore.core;
public import eventcore.driver;
import eventcore.drivers.posix.loop.select;
import eventcore.drivers.posix.loop.cfrunloop;
import eventcore.drivers.posix.loop.epoll;
import eventcore.drivers.posix.loop.kqueue;
import eventcore.drivers.posix.loop.select;
import eventcore.drivers.libasync;
import eventcore.drivers.winapi.driver;
import eventcore.internal.utils : mallocT, freeT;
version (EventcoreEpollDriver) alias NativeEventDriver = EpollEventDriver;
else version (EventcoreCFRunLoopDriver) alias NativeEventDriver = CFRunLoopEventDriver;
else version (EventcoreKqueueDriver) alias NativeEventDriver = KqueueEventDriver;
else version (EventcoreWinAPIDriver) alias NativeEventDriver = WinAPIEventDriver;
else version (EventcoreLibasyncDriver) alias NativeEventDriver = LibasyncEventDriver;

View file

@ -0,0 +1,65 @@
/**
`CFRunLoop` based event loop for macOS UI compatible operation.
*/
module eventcore.drivers.posix.loop.cfrunloop;
@safe: /*@nogc:*/ nothrow:
version (EventcoreCFRunLoopDriver):
import eventcore.drivers.posix.loop.kqueue;
import eventcore.internal.corefoundation;
import eventcore.internal.utils;
import core.time;
alias CFRunLoopEventDriver = PosixEventDriver!CFRunLoopEventLoop;
final class CFRunLoopEventLoop : KqueueEventLoopBase {
@safe nothrow:
private {
CFFileDescriptorRef m_kqueueDescriptor;
CFRunLoopSourceRef m_kqueueSource;
}
this()
@trusted @nogc {
super();
CFFileDescriptorContext ctx;
ctx.info = cast(void*)this;
m_kqueueDescriptor = CFFileDescriptorCreate(kCFAllocatorDefault,
m_queue, false, &processKqueue, &ctx);
CFFileDescriptorEnableCallBacks(m_kqueueDescriptor, CFOptionFlags.kCFFileDescriptorReadCallBack);
m_kqueueSource = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, m_kqueueDescriptor, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), m_kqueueSource, kCFRunLoopDefaultMode);
}
override bool doProcessEvents(Duration timeout)
@trusted {
// submit changes and process pending events
auto kres = doProcessEventsBase(0.seconds);
CFTimeInterval to = kres ? 0.0 : 1e-7 * timeout.total!"hnsecs";
auto res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, to, true);
return kres || res == CFRunLoopRunResult.kCFRunLoopRunHandledSource;
}
override void dispose()
{
() @trusted {
CFRelease(m_kqueueSource);
CFRelease(m_kqueueDescriptor);
} ();
super.dispose();
}
private static extern(C) void processKqueue(CFFileDescriptorRef fdref,
CFOptionFlags callBackTypes, void* info)
{
auto this_ = () @trusted { return cast(CFRunLoopEventLoop)info; } ();
auto res = this_.doProcessEventsBase(0.seconds);
() @trusted { CFFileDescriptorEnableCallBacks(this_.m_kqueueDescriptor, CFOptionFlags.kCFFileDescriptorReadCallBack); } ();
}
}

View file

@ -0,0 +1,35 @@
module eventcore.internal.corefoundation;
version (Darwin):
extern(C):
static if (!is(typeof(CFRelease))) {
alias CFTypeRef = const(void)*;
alias CFTypeRef CFAllocatorRef;
extern const CFAllocatorRef kCFAllocatorDefault;
CFTypeRef CFRetain(CFTypeRef cf);
void CFRelease(CFTypeRef cf);
}
static if (!is(typeof(CFRunLoop))) {
alias CFRunLoopMode = CFStringRef;
struct __CFRunLoop;
alias CFRunLoopRef = __CFRunLoop*;
struct __CFRunLoopSource;
alias CFRunLoopSourceRef = __CFRunLoopSource*;
alias CFTimeInterval = double;
alias Boolean = bool;
extern const CFStringRef kCFRunLoopDefaultMode;
extern const CFStringRef kCFRunLoopCommonModes;
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);
CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);
}
static if (!is(CFFileDescriptor)) {
alias FSEventStreamRef = x;
FSEventStreamRef FSEventStreamCreate(CFAllocatorRef allocator, FSEventStreamCallback callback, FSEventStreamContext *context, CFArrayRef pathsToWatch, FSEventStreamEventId sinceWhen, CFTimeInterval latency, FSEventStreamCreateFlags flags);
}