app.dhover×373 error×19all
// macOS/AppKit F05 demo — loop wakeup & external fds
// (../../../features/f05-loop-wakeup.md). A real event loop multiplexes more than
// window events; this demo measures cross-thread wakeup latency into `[NSApp run]`
// via three different mechanisms and probes whether an arbitrary file descriptor can
// join the AppKit run loop.
//
// A single worker thread (core.thread on darwin) drives all three for ~30 s:
//   mech=postevent       every 100 ms it builds an NSEventTypeApplicationDefined
//                        event carrying a monotonic send-timestamp in data1 and
//                        posts it with [NSApp postEvent:atStart:NO]. The main loop
//                        sees it in an overridden -[NSApplication sendEvent:] and
//                        logs `wakeup latency_us=… mech=postevent`.
//   mech=cfrunloopsource every 100 ms it pushes a send-timestamp onto an SPSC ring
//                        and signals a version-0 CFRunLoopSourceRef (+ CFRunLoopWakeUp).
//                        The source's perform callback runs on the main run loop,
//                        drains the ring, and logs `wakeup … mech=cfrunloopsource`.
//   fd_tick              every ~143 ms (7 Hz) it writes an 8-byte send-timestamp into
//                        a pipe; the read end is integrated via a CFFileDescriptorRef
//                        run-loop source. Its callout drains the pipe and logs
//                        `fd_tick t=… latency_us=…`.
//
// The finding the fd path proves: AppKit's run loop accepts a raw fd ONLY through a
// CoreFoundation (CFFileDescriptor) or libdispatch (dispatch_source_t) adapter — never
// directly, the way poll()/epoll do. We implement the CFFileDescriptor route and
// document the dispatch_source alternative in ../../f05-loop-wakeup.md.
//
// At exit, per-mechanism latency stats (min/median/p99/max) are reported, then a clean
// bounded exit via [NSApp stop:] + a synthetic event post (the scaffold idiom).
//
// Modes (environment variables):
//   WSI_AUTO_EXIT=1   bounded run for WSI_DURATION_S seconds (default 30), then stats
//                     and exit 0. This is the choreographed, fully agent-verifiable run.
//   WSI_DURATION_S=N  override the run duration (seconds).
//
// Headless-safe: prints `SKIP:` and exits 0 when there is no window server
// (CGMainDisplayID() == 0), per the research-docs guidelines.
module 
(module) app
app
;
import
(package) core
core
.
(module) core.attribute

This module contains UDA's (User Defined Attributes) either used in the runtime or special UDA's recognized by compiler.

Attribute Name, Linkage, Description

gnuAbiTag, C++, Declares an ABI tag on a C++ symbol. mustuse,, Ensures that values of a struct or union type are not discarded. optional, Objective-C, Makes an Objective-C interface method optional. selector, Objective-C, Attaches an Objective-C selector to a method. standalone,, Marks a shared module constructor as not depending on any other module constructor being run first. weak,, Specifies that a global symbol should be emitted with weak linkage.

Source

core/attribute.d

@copyrightCopyright Jacob Carlborg 2015.@licenseBoost License 1.0@authorsJacob Carlborg
attribute
: selector;
module `core.attribute` import `selector` not found
import
(package) core
core
.
(module) core.atomic

The atomic module provides basic support for lock-free concurrent programming.

Use the -preview=nosharedaccess compiler flag to detect unsafe individual read or write operations on shared data.

Source

core/atomic.d

Examples

int y = 2;
shared int x = y; // OK

//x++; // read modify write error
x.atomicOp!"+="(1); // OK
//y = x; // read error with preview flag
y = x.atomicLoad(); // OK
assert(y == 3);
//x = 5; // write error with preview flag
x.atomicStore(5); // OK
assert(x.atomicLoad() == 5);
@copyrightCopyright Sean Kelly 2005 - 2016.@licenseBoost License 1.0@authorsSean Kelly, Alex Rønne Petersen, Manu Evans
atomic
:
(alias template) app.atomicLoad = core.atomic.atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(auto ref return scope const T val) if (!is(T == shared(U), U) && !is(T == shared(inout(U)), U) && !is(T == shared(const(U)), U))

Loads 'val' from memory and returns it. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq, and MemoryOrder.seq.

@paramval The target variable.@returnsThe value of 'val'.
atomicLoad
,
(alias template) app.atomicStore = core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))

Writes 'newval' into 'val'. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and MemoryOrder.seq.

@paramval The target variable.@paramnewval The value to store.
atomicStore
,
(alias template) app.atomicFence = core.atomic.atomicFence(MemoryOrder order = MemoryOrder.seq)()

Inserts a full load/store memory fence (on platforms that need it). This ensures that all loads and stores before a call to this function are executed before any loads and stores after the call.

atomicFence
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.stdio

D header file for C99 <stdio.h>

pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h

Source

core/stdc/stdio.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Alex Rønne Petersen@standardsISO/IEC 9899:1999 (E)
stdio
:
(alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogc
printf
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.stdlib

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h

Source

core/stdc/stdlib.d

@copyrightCopyright Sean Kelly 2005 - 2014.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly@standardsISO/IEC 9899:1999 (E)
stdlib
:
(alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogc
getenv
,
(alias) app.atoi = int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogc
atoi
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.fcntl

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly, Alex Rønne Petersen@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
fcntl
:
(alias) app.fcntl = int core.sys.posix.fcntl.fcntl(int, int, ...) nothrow @nogc
fcntl
,
(alias constant) app.F_GETFL = int core.sys.posix.fcntl.F_GETFL = 3
F_GETFL
,
(alias constant) app.F_SETFL = int core.sys.posix.fcntl.F_SETFL = 4
F_SETFL
,
(alias constant) app.O_NONBLOCK = int core.sys.posix.fcntl.O_NONBLOCK = 2048
O_NONBLOCK
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.unistd

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly@standardsThe Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
unistd
:
(alias) app.pipe = int core.sys.posix.unistd.pipe(ref int[2]) nothrow @nogc @trusted
pipe
,
(alias) app.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogc
write
,
(alias) app.read = long core.sys.posix.unistd.read(int, void*, ulong) nothrow @nogc
read
,
(alias) app.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
;
import
(package) core
core
.
(module) core.thread

The thread module provides support for thread creation and management.

Source

core/thread/package.d

@copyrightCopyright Sean Kelly 2005 - 2012.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak
thread
:
(class) core.thread.osthread.Thread

This class encapsulates all threading functionality for the D programming language. As thread manipulation is a required facility for garbage collection, all user threads should derive from this class, and instances of this class should never be explicitly deleted. A new thread may be created using either derivation or composition, as in the following example.

Thread
;
import
(package) core
core
.
(module) core.time

Module containing core time functionality, such as Duration (which represents a duration of time) or MonoTime (which represents a timestamp of the system's monotonic clock).

Various functions take a string (or strings) to represent a unit of time (e.g. convert!("days", "hours")(numDays)). The valid strings to use with such functions are "years", "months", "weeks", "days", "hours", "minutes", "seconds", "msecs" (milliseconds), "usecs" (microseconds), "hnsecs" (hecto-nanoseconds - i.e. 100 ns) or some subset thereof. There are a few functions that also allow "nsecs", but very little actually has precision greater than hnsecs.

Symbol Description
Types
Duration Represents a duration of time of weeks or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).
TickDuration DEPRECATED Represents a duration of time in system clock ticks, using the highest precision that the system provides.
MonoTime Represents a monotonic timestamp in system clock ticks, using the highest precision that the system provides.
Functions
convert Generic way of converting between two time units.
dur Allows constructing a Duration from the given time units with the given length.
weeks&nbsp;days&nbsp;hours

minutes&nbsp;seconds&nbsp;msecs

usecs&nbsp;hnsecs&nbsp;nsecs | Convenience aliases for dur. | | abs | Returns the absolute value of a duration. |

From Duration
From TickDuration
From units
To Duration
tickDuration.to, std,conv!Duration()
dur!"msecs"(5) or 5.msecs()

| To TickDuration | duration.to, std,conv!TickDuration() |

  • | TickDuration.from!"msecs"(msecs) |

| To units | duration.total!"days" | tickDuration.msecs | convert!("days", "msecs")(msecs) |

Source

core/time.d

@copyrightCopyright 2010 - 2012@licenseBoost License 1.0.@authorsJonathan M Davis and Kato Shoichi
time
:
(struct) core.time.MonoTimeImpl!(ClockType.normal)
MonoTime
, msecs, seconds;
import instrument : instr, instrInit, instrNowUs;
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
unable to read module `instrument` Expected 'instrument.d' or 'instrument/package.d' in one of the following import paths:
import objc.autorelease : autoreleasepool_push, autoreleasepool_pop; // objective-d
unable to read module `autorelease` Expected 'objc/autorelease.d' or 'objc/autorelease/package.d' in one of the following import paths:
unable to read module `autorelease` Expected 'objc/autorelease.d' or 'objc/autorelease/package.d' in one of the following import paths:
unable to read module `autorelease` Expected 'objc/autorelease.d' or 'objc/autorelease/package.d' in one of the following import paths:
import objc.rt : SEL;
unable to read module `rt` Expected 'objc/rt.d' or 'objc/rt/package.d' in one of the following import paths:
unable to read module `rt` Expected 'objc/rt.d' or 'objc/rt/package.d' in one of the following import paths:
unable to read module `rt` Expected 'objc/rt.d' or 'objc/rt/package.d' in one of the following import paths:
// --- CoreFoundation C API (comes with the Cocoa umbrella framework) ------------- alias
(alias) app.CFRunLoopRef = void*
CFRunLoopRef
= void*;
alias
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
= void*;
alias
(alias) app.CFFileDescriptorRef = void*
CFFileDescriptorRef
= void*;
alias
(alias) app.CFStringRef = void*
CFStringRef
= void*;
alias
(alias) app.CFAllocatorRef = void*
CFAllocatorRef
= void*;
alias
(alias) app.CFIndex = long
CFIndex
= long;
alias
(alias) app.CFOptionFlags = ulong
CFOptionFlags
= ulong;
alias
(alias) app.CFFileDescriptorNativeDescriptor = int
CFFileDescriptorNativeDescriptor
= int;
// A version-0 run-loop source: `info` is passed to every callback; `perform` is the // only one we use. All function-pointer slots are extern(C) callbacks or null. extern (C) struct
(struct) app.CFRunLoopSourceContext
CFRunLoopSourceContext
{
(alias) app.CFIndex = long
CFIndex
(field) long app.CFRunLoopSourceContext.version_
version_
;
void*
(field) void* app.CFRunLoopSourceContext.info
info
;
void*
(field) void* app.CFRunLoopSourceContext.retain
retain
;
void*
(field) void* app.CFRunLoopSourceContext.release
release
;
void*
(field) void* app.CFRunLoopSourceContext.copyDescription
copyDescription
;
void*
(field) void* app.CFRunLoopSourceContext.equal
equal
;
void*
(field) void* app.CFRunLoopSourceContext.hash
hash
;
void*
(field) void* app.CFRunLoopSourceContext.schedule
schedule
;
void*
(field) void* app.CFRunLoopSourceContext.cancel
cancel
;
void*
(field) void* app.CFRunLoopSourceContext.perform
perform
; // void (*)(void* info)
} extern (C) struct
(struct) app.CFFileDescriptorContext
CFFileDescriptorContext
{
(alias) app.CFIndex = long
CFIndex
(field) long app.CFFileDescriptorContext.version_
version_
;
void*
(field) void* app.CFFileDescriptorContext.info
info
;
void*
(field) void* app.CFFileDescriptorContext.retain
retain
;
void*
(field) void* app.CFFileDescriptorContext.release
release
;
void*
(field) void* app.CFFileDescriptorContext.copyDescription
copyDescription
;
} extern (C) nothrow @nogc {
(alias) app.CFRunLoopRef = void*
CFRunLoopRef
void* app.CFRunLoopGetCurrent() nothrow @nogc
CFRunLoopGetCurrent
();
void
void app.CFRunLoopAddSource(void* rl, void* source, void* mode) nothrow @nogc
CFRunLoopAddSource
(
(alias) app.CFRunLoopRef = void*
CFRunLoopRef
(parameter) void* rl
rl
,
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
(parameter) void* source
source
,
(alias) app.CFStringRef = void*
CFStringRef
(parameter) void* mode
mode
);
void
void app.CFRunLoopSourceSignal(void* source) nothrow @nogc
CFRunLoopSourceSignal
(
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
(parameter) void* source
source
);
void
void app.CFRunLoopWakeUp(void* rl) nothrow @nogc
CFRunLoopWakeUp
(
(alias) app.CFRunLoopRef = void*
CFRunLoopRef
(parameter) void* rl
rl
);
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
void* app.CFRunLoopSourceCreate(void* allocator, long order, app.CFRunLoopSourceContext* context) nothrow @nogc
CFRunLoopSourceCreate
(
(alias) app.CFAllocatorRef = void*
CFAllocatorRef
(parameter) void* allocator
allocator
,
(alias) app.CFIndex = long
CFIndex
(parameter) long order
order
,
(struct) app.CFRunLoopSourceContext
CFRunLoopSourceContext
*
(parameter) app.CFRunLoopSourceContext* context
context
);
(alias) app.CFFileDescriptorRef = void*
CFFileDescriptorRef
void* app.CFFileDescriptorCreate(void* allocator, int fd, bool closeOnInvalidate, void* callout, app.CFFileDescriptorContext* context) nothrow @nogc
CFFileDescriptorCreate
(
(alias) app.CFAllocatorRef = void*
CFAllocatorRef
(parameter) void* allocator
allocator
,
(alias) app.CFFileDescriptorNativeDescriptor = int
CFFileDescriptorNativeDescriptor
(parameter) int fd
fd
, bool
(parameter) bool closeOnInvalidate
closeOnInvalidate
, void*
(parameter) void* callout
callout
,
(struct) app.CFFileDescriptorContext
CFFileDescriptorContext
*
(parameter) app.CFFileDescriptorContext* context
context
);
void
void app.CFFileDescriptorEnableCallBacks(void* f, ulong types) nothrow @nogc
CFFileDescriptorEnableCallBacks
(
(alias) app.CFFileDescriptorRef = void*
CFFileDescriptorRef
(parameter) void* f
f
,
(alias) app.CFOptionFlags = ulong
CFOptionFlags
(parameter) ulong types
types
);
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
void* app.CFFileDescriptorCreateRunLoopSource(void* allocator, void* f, long order) nothrow @nogc
CFFileDescriptorCreateRunLoopSource
(
(alias) app.CFAllocatorRef = void*
CFAllocatorRef
(parameter) void* allocator
allocator
,
(alias) app.CFFileDescriptorRef = void*
CFFileDescriptorRef
(parameter) void* f
f
,
(alias) app.CFIndex = long
CFIndex
(parameter) long order
order
);
extern __gshared
(alias) app.CFStringRef = void*
CFStringRef
(__gshared global) void* app.kCFRunLoopCommonModes
kCFRunLoopCommonModes
;
uint
uint app.CGMainDisplayID() nothrow @nogc
CGMainDisplayID
();
} enum
(alias) app.CFOptionFlags = ulong
CFOptionFlags
(constant) ulong app.kCFFileDescriptorReadCallBack = 1LU
kCFFileDescriptorReadCallBack
= 1UL << 0;
// --- Cocoa geometry (CGFloat == double on 64-bit) ------------------------------- extern (C) struct
(struct) app.NSPoint
NSPoint
{ double
(field) double app.NSPoint.x
x
,
(field) double app.NSPoint.y
y
;
} extern (C) struct
(struct) app.NSSize
NSSize
{ double
(field) double app.NSSize.width
width
,
(field) double app.NSSize.height
height
;
} extern (C) struct
(struct) app.NSRect
NSRect
{
(struct) app.NSPoint
NSPoint
(field) app.NSPoint app.NSRect.origin
origin
;
(struct) app.NSSize
NSSize
(field) app.NSSize app.NSRect.size
size
;
} // --- AppKit class declarations -------------------------------------------------- extern (Objective-C): extern class
(class) app.NSObject
NSObject
class `app.NSObject` Objective-C classes not supported
{ } extern class
(class) app.NSString
NSString
:
(class) app.NSObject
NSObject
class `app.NSString` Objective-C classes not supported
{ static
(class) app.NSString
NSString
app.NSString app.NSString.alloc()
alloc
() @selector("alloc");
(class) app.NSString
NSString
app.NSString app.NSString.initWithUTF8String(const(char)* s)
initWithUTF8String
(const(char)*
(parameter) const(char)* s
s
) @selector("initWithUTF8String:");
} extern class
(class) app.NSResponder
NSResponder
:
(class) app.NSObject
NSObject
class `app.NSResponder` Objective-C classes not supported
{ } extern class
(class) app.NSEvent
NSEvent
:
(class) app.NSObject
NSObject
class `app.NSEvent` Objective-C classes not supported
{ static
(class) app.NSEvent
NSEvent
app.NSEvent app.NSEvent.otherEventWithType(long type, app.NSPoint location, ulong modifierFlags, double timestamp, long windowNumber, app.NSObject context, short subtype, long data1, long data2)
otherEventWithType
(long
(parameter) long type
type
,
(struct) app.NSPoint
NSPoint
(parameter) app.NSPoint location
location
, ulong
(parameter) ulong modifierFlags
modifierFlags
,
double
(parameter) double timestamp
timestamp
, long
(parameter) long windowNumber
windowNumber
,
(class) app.NSObject
NSObject
(parameter) app.NSObject context
context
, short
(parameter) short subtype
subtype
,
long
(parameter) long data1
data1
, long
(parameter) long data2
data2
)
@selector("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"); long
long app.NSEvent.type()
type
() @selector("type");
short
short app.NSEvent.subtype()
subtype
() @selector("subtype");
long
long app.NSEvent.data1()
data1
() @selector("data1");
} // NSApplication declares sendEvent: so WakeApp can override it. sharedApplication is // declared ONLY on the WakeApp leaf (returning WakeApp) to dodge the covariant-return // clash on a base declaration — calling +sharedApplication on the subclass makes NSApp // an instance of it, so our sendEvent: override is installed. extern class
(class) app.NSApplication
NSApplication
:
(class) app.NSResponder
NSResponder
class `app.NSApplication` Objective-C classes not supported
{ void
void app.NSApplication.setActivationPolicy(long policy)
setActivationPolicy
(long
(parameter) long policy
policy
) @selector("setActivationPolicy:");
void
void app.NSApplication.activateIgnoringOtherApps(bool flag)
activateIgnoringOtherApps
(bool
(parameter) bool flag
flag
) @selector("activateIgnoringOtherApps:");
void
void app.NSApplication.run()
run
() @selector("run");
void
void app.NSApplication.stop(app.NSObject sender)
stop
(
(class) app.NSObject
NSObject
(parameter) app.NSObject sender
sender
) @selector("stop:");
void
void app.NSApplication.sendEvent(app.NSEvent event)
sendEvent
(
(class) app.NSEvent
NSEvent
(parameter) app.NSEvent event
event
) @selector("sendEvent:");
void
void app.NSApplication.postEvent(app.NSEvent event, bool atStart)
postEvent
(
(class) app.NSEvent
NSEvent
(parameter) app.NSEvent event
event
, bool
(parameter) bool atStart
atStart
) @selector("postEvent:atStart:");
} extern class
(class) app.NSView
NSView
:
(class) app.NSResponder
NSResponder
class `app.NSView` Objective-C classes not supported
{
(struct) app.NSRect
NSRect
app.NSRect app.NSView.bounds()
bounds
() @selector("bounds");
} extern class
(class) app.NSWindow
NSWindow
:
(class) app.NSResponder
NSResponder
class `app.NSWindow` Objective-C classes not supported
{ static
(class) app.NSWindow
NSWindow
app.NSWindow app.NSWindow.alloc()
alloc
() @selector("alloc");
(class) app.NSWindow
NSWindow
app.NSWindow app.NSWindow.initWithContentRect(app.NSRect contentRect, ulong styleMask, ulong backing, bool defer)
initWithContentRect
(
(struct) app.NSRect
NSRect
(parameter) app.NSRect contentRect
contentRect
, ulong
(parameter) ulong styleMask
styleMask
,
ulong
(parameter) ulong backing
backing
, bool
(parameter) bool defer
defer
) @selector("initWithContentRect:styleMask:backing:defer:");
void
void app.NSWindow.setTitle(app.NSString title)
setTitle
(
(class) app.NSString
NSString
(parameter) app.NSString title
title
) @selector("setTitle:");
void
void app.NSWindow.setContentView(app.NSView view)
setContentView
(
(class) app.NSView
NSView
(parameter) app.NSView view
view
) @selector("setContentView:");
void
void app.NSWindow.makeKeyAndOrderFront(app.NSObject sender)
makeKeyAndOrderFront
(
(class) app.NSObject
NSObject
(parameter) app.NSObject sender
sender
) @selector("makeKeyAndOrderFront:");
} // The NSApplication subclass: its sendEvent: override is where mech=postevent and the // bounded-exit DONE signal are observed (the run loop calls -[NSApp sendEvent:] for // every dequeued event, including NSEventTypeApplicationDefined). class
(class) app.WakeApp
WakeApp
:
(class) app.NSApplication
NSApplication
class `app.WakeApp` Objective-C classes not supported
{ static
(class) app.WakeApp
WakeApp
app.WakeApp app.WakeApp.sharedApplication()
sharedApplication
() @selector("sharedApplication");
override void
void app.WakeApp.sendEvent(app.NSEvent event)
sendEvent
(
(class) app.NSEvent
NSEvent
(parameter) app.NSEvent event
event
) @selector("sendEvent:")
{ if (event.
event.type
type
() == NSEventTypeApplicationDefined)
{ immutable
_error_ st
st
= event.
event.subtype
subtype
();
if (st == subtypeWake) { onWake(MechId.
MechId.postevent
postevent
, cast(ulong) event.
event.data1
data1
());
return; // consume — not a real UI event } if (st == subtypeDone) { onDone(); return; } // subtypeStop (0): the synthetic unblock event — fall through to super. } super.
super.sendEvent
sendEvent
(event);
} } // A minimal solid-color content view so the demo is a real windowed AppKit app. class
(class) app.FillView
FillView
:
(class) app.NSView
NSView
class `app.FillView` Objective-C classes not supported
{ static
(class) app.FillView
FillView
app.FillView app.FillView.alloc()
alloc
() @selector("alloc");
(class) app.FillView
FillView
app.FillView app.FillView.initWithFrame(app.NSRect frame)
initWithFrame
(
(struct) app.NSRect
NSRect
(parameter) app.NSRect frame
frame
) @selector("initWithFrame:");
void
void app.FillView.drawRect(app.NSRect dirtyRect)
drawRect
(
(struct) app.NSRect
NSRect
(parameter) app.NSRect dirtyRect
dirtyRect
) @selector("drawRect:")
{ // The loop, not the pixels, is the subject here; nothing to draw. } } extern (D): // back to D linkage enum
(constant) int app.NSApplicationActivationPolicyRegular = 0
NSApplicationActivationPolicyRegular
= 0;
enum : ulong {
(enum value) app.NSWindowStyleMaskTitled = 1LU
NSWindowStyleMaskTitled
= 1,
(enum value) app.NSWindowStyleMaskClosable = 2LU
NSWindowStyleMaskClosable
= 2,
(enum value) app.NSWindowStyleMaskResizable = 8LU
NSWindowStyleMaskResizable
= 8,
} enum ulong
(constant) ulong app.NSBackingStoreBuffered = 2LU
NSBackingStoreBuffered
= 2;
enum long
(constant) long app.NSEventTypeApplicationDefined = 15L
NSEventTypeApplicationDefined
= 15;
// applicationDefined subtypes we distinguish in sendEvent: enum short
(constant) short app.subtypeStop = cast(short)0
subtypeStop
= 0; // synthetic post that unblocks [NSApp run] after stop:
enum short
(constant) short app.subtypeWake = cast(short)87
subtypeWake
= 0x57; // 'W' — a mech=postevent wakeup
enum short
(constant) short app.subtypeDone = cast(short)68
subtypeDone
= 0x44; // 'D' — worker signalling end-of-run
// --- Demo state ----------------------------------------------------------------- enum
(enum) app.MechId
MechId
: int
{
(enum value) app.MechId.postevent = 0
postevent
= 0,
(enum value) app.MechId.cfrunloopsource = 1
cfrunloopsource
= 1,
} immutable
(alias) object.string = string
string
[2]
(immutable global) immutable(string[2]) app.mechNames
mechNames
= ["postevent", "cfrunloopsource"];
__gshared
(class) app.WakeApp
WakeApp
(__gshared global) app.WakeApp app.g_app
g_app
;
__gshared
(class) app.NSWindow
NSWindow
(__gshared global) app.NSWindow app.g_win
g_win
;
__gshared
(alias) app.CFRunLoopRef = void*
CFRunLoopRef
(__gshared global) void* app.g_mainRL
g_mainRL
;
__gshared
(alias) app.CFRunLoopSourceRef = void*
CFRunLoopSourceRef
(__gshared global) void* app.g_cfSource
g_cfSource
;
__gshared int
(__gshared global) int app.g_pipeRead
g_pipeRead
= -1,
(__gshared global) int app.g_pipeWrite
g_pipeWrite
= -1;
__gshared
(class) core.thread.osthread.Thread

This class encapsulates all threading functionality for the D programming language. As thread manipulation is a required facility for garbage collection, all user threads should derive from this class, and instances of this class should never be explicitly deleted. A new thread may be created using either derivation or composition, as in the following example.

Thread
(__gshared global) core.thread.osthread.Thread app.g_worker
g_worker
;
shared bool
(shared global) shared(bool) app.g_stopWorker
g_stopWorker
;
__gshared int
(__gshared global) int app.g_durationS
g_durationS
= 30;
// Latency samples (main thread only appends — all callbacks run on the main loop). enum
(constant) int app.sampleCap = 8192
sampleCap
= 8192;
__gshared double[
(constant) int app.sampleCap = 8192
sampleCap
][2]
(constant) int app.sampleCap = 8192
g_lat
; // [mech][i], microseconds
__gshared
(alias) object.size_t = ulong
size_t
[2]
(__gshared global) ulong[2] app.g_latN
g_latN
;
__gshared double[
(constant) int app.sampleCap = 8192
sampleCap
]
(__gshared global) double[8192] app.g_fdLat
g_fdLat
;
__gshared
(alias) object.size_t = ulong
size_t
(__gshared global) ulong app.g_fdN
g_fdN
;
__gshared uint
(__gshared global) uint app.g_wakeCount
g_wakeCount
,
(__gshared global) uint app.g_fdCount
g_fdCount
;
// SPSC ring carrying send-timestamps for the coalescing-prone CFRunLoopSource path: // one signal may collapse several pushes into a single perform, so we drain the ring. enum
(constant) int app.ringCap = 4096
ringCap
= 4096; // power of two
__gshared ulong[
(constant) int app.ringCap = 4096
ringCap
]
(__gshared global) ulong[4096] app.g_ring
g_ring
;
shared
(alias) object.size_t = ulong
size_t
(shared global) shared(ulong) app.g_ringHead
g_ringHead
; // producer (worker)
shared
(alias) object.size_t = ulong
size_t
(shared global) shared(ulong) app.g_ringTail
g_ringTail
; // consumer (main perform)
void
void app.ringPush(ulong v) nothrow @nogc
ringPush
(ulong
(parameter) ulong v
v
) nothrow @nogc
{ immutable
_error_ h
h
= atomicLoad(g_ringHead);
g_ring[h & (ringCap - 1)] = v; atomicFence(); atomicStore(g_ringHead, h + 1); } void
void app.onWake(app.MechId mech, ulong sendUs)
onWake
(
(enum) app.MechId
MechId
(parameter) app.MechId mech
mech
, ulong
(parameter) ulong sendUs
sendUs
)
{ immutable
_error_ now
now
= instrNowUs();
immutable
_error_ lat
lat
= now > sendUs ? cast(double)(now - sendUs) : 0.0;
if (g_latN[mech] < sampleCap) g_lat[mech][g_latN[mech]++] = lat; ++g_wakeCount; instr("wakeup", "latency_us=%.1f mech=%.*s n=%u", lat, cast(int) mechNames[mech].length, mechNames[mech].ptr, g_wakeCount); } void
void app.onDone()
onDone
()
{ instr("step", "name=worker_done wakes=%u fd_ticks=%u", g_wakeCount, g_fdCount); g_app.
g_app.stop
stop
(null);
// stop: only takes effect after the next event dispatch; post a synthetic // application-defined event so [NSApp run] wakes up and returns (scaffold finding).
(unresolved type) NSEvent
NSEvent
_error_ ev
ev
= NSEvent.
NSEvent.otherEventWithType
otherEventWithType
(NSEventTypeApplicationDefined,
NSPoint(0, 0), 0, 0, 0, null, subtypeStop, 0, 0); g_app.
g_app.postEvent
postEvent
(ev, true);
} // CFRunLoopSource perform callback (main run loop): drain every queued send-timestamp. extern (C) void
void app.performCFSource(void* info) nothrow
performCFSource
(void*
(parameter) void* info
info
) nothrow
{
(unresolved type) size_t
size_t
_error_ tail
tail
= atomicLoad(g_ringTail);
immutable
_error_ head
head
= atomicLoad(g_ringHead);
while (tail != head) { immutable
_error_ sendUs
sendUs
= g_ring[tail & (ringCap - 1)];
++tail; immutable
_error_ now
now
= instrNowUs();
immutable
_error_ lat
lat
= now > sendUs ? cast(double)(now - sendUs) : 0.0;
if (g_latN[MechId.
MechId.cfrunloopsource
cfrunloopsource
] < sampleCap)
g_lat[MechId.
MechId.cfrunloopsource
cfrunloopsource
][g_latN[MechId.
MechId.cfrunloopsource
cfrunloopsource
]++] = lat;
++g_wakeCount; instr("wakeup", "latency_us=%.1f mech=cfrunloopsource n=%u", lat, g_wakeCount); } atomicStore(g_ringTail, tail); } // CFFileDescriptor callout (main run loop): drain all 8-byte timestamps from the pipe. // CFFileDescriptor disables its callbacks after each fire, so we must re-enable them. extern (C) void
void app.fdCallout(void* f, ulong types, void* info) nothrow
fdCallout
(
(alias) app.CFFileDescriptorRef = void*
CFFileDescriptorRef
(parameter) void* f
f
,
(alias) app.CFOptionFlags = ulong
CFOptionFlags
(parameter) ulong types
types
, void*
(parameter) void* info
info
) nothrow
{ ubyte[8 * 64]
_error_ buf
buf
;
for (;;) { immutable
_error_ n
n
= read(g_pipeRead, buf.ptr, buf.length);
if (n <= 0) break;
(unresolved type) size_t
size_t
_error_ off
off
= 0;
while (off + 8 <= cast(
(unresolved type) size_t
size_t
) n)
{ ulong
_error_ sendUs
sendUs
= *cast(ulong*)(buf.ptr + off);
off += 8; immutable
_error_ now
now
= instrNowUs();
immutable
_error_ lat
lat
= now > sendUs ? cast(double)(now - sendUs) : 0.0;
if (g_fdN < sampleCap) g_fdLat[g_fdN++] = lat; ++g_fdCount; instr("fd_tick", "t=%u latency_us=%.1f", g_fdCount, lat); } if (cast(
(unresolved type) size_t
size_t
) n < buf.length)
break; } CFFileDescriptorEnableCallBacks(f, kCFFileDescriptorReadCallBack); } // The worker thread: posts mech A + mech B at 10 Hz and the fd at ~7 Hz for the run // duration, then signals DONE. All wakeup primitives below are documented thread-safe: // -[NSApp postEvent:atStart:], CFRunLoopSourceSignal, CFRunLoopWakeUp, and write(). void
void app.workerMain()
workerMain
()
{ auto
_error_ pool
pool
= autoreleasepool_push(); // Cocoa from a secondary thread needs a pool
scope (exit) autoreleasepool_pop(pool); immutable
_error_ start
start
= MonoTime.currTime;
immutable
_error_ deadline
deadline
= start + g_durationS.seconds;
(unresolved type) MonoTime
MonoTime
_error_ nextAB
nextAB
= start;
(unresolved type) MonoTime
MonoTime
_error_ nextFd
nextFd
= start;
immutable
_error_ abPeriod
abPeriod
= 100.msecs; // 10 Hz
immutable
_error_ fdPeriod
fdPeriod
= 143.msecs; // ~7 Hz
while (MonoTime.currTime < deadline && !atomicLoad(g_stopWorker)) { immutable
_error_ now
now
= MonoTime.currTime;
if (now >= nextAB) { immutable
_error_ sendUs
sendUs
= instrNowUs();
// mech A: post an applicationDefined event carrying the send time in data1.
(unresolved type) NSEvent
NSEvent
_error_ ev
ev
= NSEvent.
NSEvent.otherEventWithType
otherEventWithType
(NSEventTypeApplicationDefined,
NSPoint(0, 0), 0, 0, 0, null, subtypeWake, cast(long) sendUs, 0); g_app.
g_app.postEvent
postEvent
(ev, false);
// mech B: push the send time and signal the run-loop source. ringPush(instrNowUs()); CFRunLoopSourceSignal(g_cfSource); CFRunLoopWakeUp(g_mainRL); nextAB += abPeriod; } if (now >= nextFd) { ulong
_error_ sendUs
sendUs
= instrNowUs();
write(g_pipeWrite, &sendUs, sendUs.sizeof); nextFd += fdPeriod; } Thread.sleep(2.msecs); } // Signal end-of-run to the main loop (also delivered through sendEvent:).
(unresolved type) NSEvent
NSEvent
_error_ done
done
= NSEvent.
NSEvent.otherEventWithType
otherEventWithType
(NSEventTypeApplicationDefined,
NSPoint(0, 0), 0, 0, 0, null, subtypeDone, 0, 0); g_app.
g_app.postEvent
postEvent
(done, false);
} void
void app.reportStats(const(char)* mech, double[] s)
reportStats
(const(char)*
(parameter) const(char)* mech
mech
, double[]
(parameter) double[] s
s
)
{ if (s.length == 0) { instr("stats", "mech=%s n=0", mech); return; } import std.algorithm.sorting : sort; sort(s); immutable
_error_ mn
mn
= s[0];
immutable
_error_ mx
mx
= s[$ - 1];
immutable
_error_ med
med
= s[s.length / 2];
(unresolved type) size_t
size_t
_error_ i99
i99
= cast(
(unresolved type) size_t
size_t
)(s.length * 99 / 100);
if (i99 >= s.length) i99 = s.length - 1; instr("stats", "mech=%s n=%zu min_us=%.1f med_us=%.1f p99_us=%.1f max_us=%.1f", mech, s.length, mn, med, s[i99], mx); } int
int D main()
main
()
{ immutable
_error_ autoExit
autoExit
= getenv("WSI_AUTO_EXIT") !is null;
if (auto d = getenv("WSI_DURATION_S")) { immutable
_error_ v
v
= atoi(d);
if (v > 0) g_durationS = v; } instrInit("APPKIT_F05"); instr("init_start", "auto_exit=%d duration_s=%d", autoExit ? 1 : 0, g_durationS); instr("step", "name=CGMainDisplayID"); if (CGMainDisplayID() == 0) { printf("SKIP: no macOS window server (CGMainDisplayID == 0)\n"); return 0; } auto
_error_ pool
pool
= autoreleasepool_push();
scope (exit) autoreleasepool_pop(pool); // Shared app — instantiated as a WakeApp so our sendEvent: override is live. instr("step", "name=NSApplication_sharedApplication class=WakeApp"); g_app = WakeApp.
WakeApp.sharedApplication
sharedApplication
();
g_app.
g_app.setActivationPolicy
setActivationPolicy
(NSApplicationActivationPolicyRegular);
immutable
_error_ contentRect
contentRect
= NSRect(NSPoint(120, 120), NSSize(360, 240));
g_win = NSWindow.
NSWindow.alloc
alloc
().
NSWindow.alloc().initWithContentRect
initWithContentRect
(contentRect,
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false); g_win.
g_win.setTitle
setTitle
(NSString.
NSString.alloc
alloc
().
NSString.alloc().initWithUTF8String
initWithUTF8String
("wsi-f05-loop-wakeup"));
(unresolved type) FillView
FillView
_error_ view
view
= FillView.
FillView.alloc
alloc
().
FillView.alloc().initWithFrame
initWithFrame
(contentRect);
g_win.
g_win.setContentView
setContentView
(view);
g_win.
g_win.makeKeyAndOrderFront
makeKeyAndOrderFront
(null);
g_app.
g_app.activateIgnoringOtherApps
activateIgnoringOtherApps
(true);
instr("window_created"); // We are on the main thread; [NSApp run] will pump THIS run loop. Add both the // CFRunLoopSource (mech B) and the CFFileDescriptor source (fd) to it. g_mainRL = CFRunLoopGetCurrent(); instr("step", "name=CFRunLoopSourceCreate version=0");
(unresolved type) CFRunLoopSourceContext
CFRunLoopSourceContext
_error_ ctx
ctx
;
ctx.
ctx.version_
version_
= 0;
ctx.
ctx.perform
perform
= cast(void*)&performCFSource;
g_cfSource = CFRunLoopSourceCreate(null, 0, &ctx); CFRunLoopAddSource(g_mainRL, g_cfSource, kCFRunLoopCommonModes); // Arbitrary fd: a pipe, read end made nonblocking and wrapped in a // CFFileDescriptor run-loop source. This is the ONLY way a raw fd joins AppKit's // loop — see ../../f05-loop-wakeup.md for the dispatch_source_t alternative. int[2]
_error_ fds
fds
;
if (pipe(fds) != 0) { printf("SKIP: pipe() failed\n"); return 0; } g_pipeRead = fds[0]; g_pipeWrite = fds[1]; fcntl(g_pipeRead, F_SETFL, fcntl(g_pipeRead, F_GETFL, 0) | O_NONBLOCK); instr("step", "name=CFFileDescriptorCreate fd=%d", g_pipeRead);
(unresolved type) CFFileDescriptorContext
CFFileDescriptorContext
_error_ fctx
fctx
;
(unresolved type) CFFileDescriptorRef
CFFileDescriptorRef
_error_ cffd
cffd
= CFFileDescriptorCreate(null, g_pipeRead, true,
cast(void*)&fdCallout, &fctx); CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
(unresolved type) CFRunLoopSourceRef
CFRunLoopSourceRef
_error_ fdSource
fdSource
= CFFileDescriptorCreateRunLoopSource(null, cffd, 0);
CFRunLoopAddSource(g_mainRL, fdSource, kCFRunLoopCommonModes); // Launch the worker and cede the main thread to AppKit. instr("step", "name=worker_start period_ms=100 fd_period_ms=143 duration_s=%d", g_durationS); g_worker = new
(unresolved type) Thread
Thread
(&workerMain);
g_worker.
g_worker.start
start
();
instr("step", "name=NSApp_run"); g_app.
g_app.run
run
();
atomicStore(g_stopWorker, true); g_worker.join(); reportStats("postevent", g_lat[MechId.
MechId.postevent
postevent
][0 .. g_latN[MechId.postevent]]);
reportStats("cfrunloopsource", g_lat[MechId.
MechId.cfrunloopsource
cfrunloopsource
][0 .. g_latN[MechId.cfrunloopsource]]);
reportStats("fd", g_fdLat[0 .. g_fdN]); instr("loop_exit", "wakes=%u fd_ticks=%u", g_wakeCount, g_fdCount); close(g_pipeRead); close(g_pipeWrite); return 0; }