// F17 X11 demo — threading probes: deliberately violate (and then obey)
// Xlib's threading rules and record exactly what breaks and how
// (../../../features/f17-threading.md; findings in ../../f17-threading.md).
//
// Probes (--probe=N; no argument = fork+run every probe TWICE, the CI default,
// so race-dependent outcomes show their spread):
// 1 window created on a worker thread WITHOUT XInitThreads while the main
// thread pumps events and XSyncs on the same Display — the classic
// "unexpected async reply" corruption / XIO / crash
// 2 the same choreography WITH XInitThreads — legal; prove it
// 3 two threads sharing one Display, BOTH blocking in XNextEvent (with
// XInitThreads): how do queued events distribute? does one starve?
// 4 render thread: XShmPutImage + XFlush from a worker while the main
// thread pumps (with XInitThreads); completion events land on the pump
// 5 one Display per thread, NO XInitThreads — the always-safe model
// 6 XInitThreads called LATE (after XOpenDisplay), then the probe-1
// choreography — does the late call rescue the existing Display?
//
// Every probe ends in a verdict line
// probe n=<N> result=ok|error|crash|deadlock|silent detail=...
// that survives ANY outcome: XSetErrorHandler counts protocol errors,
// XSetIOErrorHandler + SIGSEGV/SIGABRT/SIGBUS handlers turn crashes into a
// flushed verdict + _exit(0), and a SIGALRM watchdog turns hangs into
// result=deadlock. Crash probes still exit 0 — crashing is their job.
//
// Headless-safe: no X server -> prints `SKIP:` and exits 0.
module (module) appapp;
import (module) cc; // ImportC: Xlib + Xutil + Xatom + XShm + sys/ipc + sys/shm + poll
import (module) instrumentinstrument;
import (package) corecore.(module) core.atomicThe 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);
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.
atomicLoad, (alias template) app.atomicOp = core.atomic.atomicOp(string op, T, V1)(ref shared T val, V1 mod) if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod")))Performs the binary operation 'op' on val using 'mod' as the modifier.
atomicOp, (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.
atomicStore;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.configD compatible types that correspond to various basic types in associated
C and C++ compilers.
Source
core/stdc/config.d
config : c_long, c_ulong;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf, (alias) app.snprintf = int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stringD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h
Source
core/stdc/string.d
string : (alias) app.memset = void* core.stdc.string.memset(return scope void* s, int c, ulong n) pure nothrow @nogcmemset, (alias) app.strlen = ulong core.stdc.string.strlen(scope const(char*) s) pure nothrow @nogcstrlen;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.pthreadD header file for POSIX.
pthread : (alias) app.pthread_create = int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create, (alias) app.pthread_join = int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join, pthread_t;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.signalD header file for POSIX.
Source
core/sys/posix/signal.d
signal : (alias constant) app.SA_RESTART = int core.sys.posix.signal.SA_RESTART = 268435456SA_RESTART, (alias) app.sigaction = int core.sys.posix.signal.sigaction(int, scope const(core.sys.posix.signal.sigaction_t*), core.sys.posix.signal.sigaction_t*) nothrow @nogcsigaction, (struct) core.sys.posix.signal.sigaction_tsigaction_t, (alias) app.sigemptyset = int core.sys.posix.signal.sigemptyset(core.sys.posix.signal.sigset_t*) nothrow @nogcsigemptyset,
(alias constant) app.SIGABRT = int core.stdc.signal.SIGABRT = 6SIGABRT, (alias constant) app.SIGALRM = int core.sys.posix.signal.SIGALRM = 14SIGALRM, (alias constant) app.SIGBUS = int core.sys.posix.signal.SIGBUS = 7SIGBUS, (alias constant) app.SIGSEGV = int core.stdc.signal.SIGSEGV = 11SIGSEGV;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.waitD header file for POSIX.
wait : (alias) app.waitpid = int core.sys.posix.sys.wait.waitpid(int, int*, int) nothrow @nogcwaitpid;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) app._exit = noreturn core.sys.posix.unistd._exit(int) nothrow @nogc @trusted_exit, (alias) app.alarm = uint core.sys.posix.unistd.alarm(uint) nothrow @nogc @trustedalarm, (alias) app.fork = int core.sys.posix.unistd.fork() nothrow @nogc @trustedfork, (alias) app.usleep = int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep, (alias) app.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite;
// -- macro constants ImportC cannot export ------------------------------------
enum : c_long
{
(enum value) app.ExposureMask = 32768LExposureMask = 1L << 15,
(enum value) app.StructureNotifyMask = 131072LStructureNotifyMask = 1L << 17,
}
enum // XEvent.type discriminators
{
(enum value) app.Expose = 12Expose = 12,
(enum value) app.MapNotify = 19MapNotify = 19,
(enum value) app.ClientMessage = 33ClientMessage = 33,
}
enum (constant) int app.ZPixmap = 2ZPixmap = 2;
enum (constant) int app.False = 0False = 0;
enum (constant) int app.True = 1True = 1;
enum (constant) int app.IPC_PRIVATE = 0IPC_PRIVATE = 0;
enum (constant) int app.IPC_CREAT = 512IPC_CREAT = 0x200;
enum (constant) int app.IPC_RMID = 0IPC_RMID = 0;
// -- verdict plumbing: must survive crashes ------------------------------------
private __gshared int (__gshared global) int app.g_probeg_probe;
private shared int (shared global) shared(int) app.g_xlibErrorsg_xlibErrors;
private __gshared char[128] (__gshared global) char[128] app.g_lastXlibErrorg_lastXlibError = '\0';
private shared bool (shared global) shared(bool) app.g_verdictDoneg_verdictDone;
/// The one line the spec requires per run. Async-signal-safe on purpose
/// (snprintf into a static buffer + write(2)) so the signal/XIO handlers can
/// call it; the normal path uses it too so the format is identical.
void void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict(scope const(char)* (parameter) const(char)* resultresult, scope const(char)* (parameter) const(char)* detaildetail) @nogc nothrow
{
if (bool core.atomic.atomicLoad!(MemoryOrder.seq, bool)(ref return scope shared(const(bool)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(bool) app.g_verdictDoneg_verdictDone))
return;
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites '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.
atomicStore((shared global) shared(bool) app.g_verdictDoneg_verdictDone, true);
static __gshared char[256] (__gshared global) char[256] app.verdict.bufbuf;
const (local variable) const(int) nn = int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[256] app.verdict.bufbuf.(constant) char* char[256].ptr = &bufptr, (__gshared global) char[256] app.verdict.bufbuf.(constant) ulong char[256].length = 256LUlength, "%lld f17_x11 probe n=%d result=%s detail=%s\n",
long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs(), (__gshared global) int app.g_probeg_probe, (parameter) const(char)* resultresult, (parameter) const(char)* detaildetail);
long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite(2, (__gshared global) char[256] app.verdict.bufbuf.(constant) char* char[256].ptr = &bufptr, (local variable) const(int) nn);
}
extern (C) int app.xlibErrorHandlerxlibErrorHandler(Display* (parameter) Display* dpydpy, XErrorEvent* e) @nogc nothrow
{
(template instance) atomicOp!"+="atomicOp!"+="(g_xlibErrors, 1);
char[64] _error_ texttext;
XGetErrorText(dpy, e.error_code, text.text.ptrptr, text.text.lengthlength);
snprintf(g_lastXlibError.g_lastXlibError.ptrptr, g_lastXlibError.g_lastXlibError.lengthlength,
"code=%d(%s)_request=%d.%d_resource=0x%lx",
e.error_code, text.text.ptrptr, e.request_code, e.minor_code, e.resourceid);
emitf("xlib_error", "%s", g_lastXlibError.g_lastXlibError.ptrptr);
return 0; // continue; the verdict decides
}
extern (C) int app.xioErrorHandlerxioErrorHandler(Display*) @nogc nothrow
{
// Xlib is about to exit() — flush the verdict first. Must not return.
verdict("crash", "xio_error_connection_lost");
_exit(0);
return 0;
}
extern (C) void void app.signalHandler(int sig) nothrow @nogcsignalHandler(int (parameter) int sigsig) @nogc nothrow
{
static __gshared char[64] (__gshared global) char[64] app.signalHandler.dd;
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[64] app.signalHandler.dd.(constant) char* char[64].ptr = &dptr, (__gshared global) char[64] app.signalHandler.dd.(constant) ulong char[64].length = 64LUlength, "fatal_signal=%d", (parameter) int sigsig);
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("crash", (__gshared global) char[64] app.signalHandler.dd.(constant) char* char[64].ptr = &dptr);
noreturn core.sys.posix.unistd._exit(int) nothrow @nogc @trusted_exit(0);
}
extern (C) void void app.alarmHandler(int __param_0) nothrow @nogcalarmHandler(int) @nogc nothrow
{
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("deadlock", "watchdog_timeout_8s");
noreturn core.sys.posix.unistd._exit(int) nothrow @nogc @trusted_exit(0);
}
void void app.installCrashHandlers() nothrow @nogcinstallCrashHandlers() @nogc nothrow
{
XSetErrorHandler(&xlibErrorHandler);
XSetIOErrorHandler(&xioErrorHandler);
(struct) core.sys.posix.signal.sigaction_tsigaction_t (local variable) core.sys.posix.signal.sigaction_t sasa;
(local variable) core.sys.posix.signal.sigaction_t sasa.(field) extern (C) void function(int) core.sys.posix.signal.sigaction_t.sa_handlersa_handler = &void app.signalHandler(int sig) nothrow @nogcsignalHandler;
int core.sys.posix.signal.sigemptyset(core.sys.posix.signal.sigset_t*) nothrow @nogcsigemptyset(&(local variable) core.sys.posix.signal.sigaction_t sasa.(field) core.sys.posix.signal.sigset_t core.sys.posix.signal.sigaction_t.sa_masksa_mask);
(local variable) core.sys.posix.signal.sigaction_t sasa.(field) int core.sys.posix.signal.sigaction_t.sa_flagssa_flags = 0;
int core.sys.posix.signal.sigaction(int, scope const(core.sys.posix.signal.sigaction_t*), core.sys.posix.signal.sigaction_t*) nothrow @nogcsigaction((constant) int core.stdc.signal.SIGSEGV = 11SIGSEGV, &(local variable) core.sys.posix.signal.sigaction_t sasa, null);
int core.sys.posix.signal.sigaction(int, scope const(core.sys.posix.signal.sigaction_t*), core.sys.posix.signal.sigaction_t*) nothrow @nogcsigaction((constant) int core.sys.posix.signal.SIGBUS = 7SIGBUS, &(local variable) core.sys.posix.signal.sigaction_t sasa, null);
int core.sys.posix.signal.sigaction(int, scope const(core.sys.posix.signal.sigaction_t*), core.sys.posix.signal.sigaction_t*) nothrow @nogcsigaction((constant) int core.stdc.signal.SIGABRT = 6SIGABRT, &(local variable) core.sys.posix.signal.sigaction_t sasa, null);
(local variable) core.sys.posix.signal.sigaction_t sasa.(field) extern (C) void function(int) core.sys.posix.signal.sigaction_t.sa_handlersa_handler = &void app.alarmHandler(int __param_0) nothrow @nogcalarmHandler;
int core.sys.posix.signal.sigaction(int, scope const(core.sys.posix.signal.sigaction_t*), core.sys.posix.signal.sigaction_t*) nothrow @nogcsigaction((constant) int core.sys.posix.signal.SIGALRM = 14SIGALRM, &(local variable) core.sys.posix.signal.sigaction_t sasa, null);
uint core.sys.posix.unistd.alarm(uint) nothrow @nogc @trustedalarm(8); // the deadlock watchdog
}
// -- probes 1/2/6: window on a worker thread, pump + XSync storm on main -------
private __gshared Display* _error_ app.g_dpyg_dpy;
private shared bool (shared global) shared(bool) app.g_workerDoneg_workerDone;
/// Worker: create/map a window, then hammer reply-carrying requests
/// (XInternAtom round-trips) for ~1.2 s. Without XInitThreads both threads
/// read the one connection -> interleaved replies -> corruption.
extern (C) void* void* app.windowWorker(void* __param_0) nothrow @nogcWorker
create/map a window, then hammer reply-carrying requests
(XInternAtom round-trips) for ~1.2 s. Without XInitThreads both threads
read the one connection -> interleaved replies -> corruption.
windowWorker(void*) @nogc nothrow
{
auto (local variable) _error_ dpydpy = g_dpy;
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("thread=worker action=XCreateSimpleWindow");
Window (local variable) _error_ winwin = XCreateSimpleWindow(dpy, XRootWindow(dpy, XDefaultScreen(dpy)),
0, 0, 200, 120, 1, 0, 0xffffff);
XSelectInput(dpy, win, ExposureMask | StructureNotifyMask);
XMapWindow(dpy, win);
XFlush(dpy);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("thread=worker", "action=window_created xid=0x%lx", win);
XEvent (local variable) _error_ msgmsg;
msg.xclient.type = ClientMessage;
msg.xclient.window = win;
msg.xclient.message_type = XInternAtom(dpy, "WSI_F17_TICK", False);
msg.xclient.format = 32;
// Request storm: buffered requests (XStoreName marshals into the shared
// Display output buffer) mixed with reply-carrying round-trips and event
// traffic — while the main thread appends to the SAME buffer and reads
// the SAME reply/event stream, unlocked.
const (local variable) const(long) deadlinedeadline = long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() + 1_200_000;
int (local variable) int ii = 0;
char[32] (local variable) char[32] namename;
while (long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() < (local variable) const(long) deadlinedeadline)
{
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((local variable) char[32] namename.(constant) char* char[32].ptr = &nameptr, (local variable) char[32] namename.(constant) ulong char[32].length = 32LUlength, "WSI_F17_%d", (local variable) int ii++);
XStoreName(dpy, win, name.name.ptrptr); // buffered (no reply)
if ((local variable) int ii % 8 == 0)
XInternAtom(dpy, name.name.ptrptr, False); // round-trip
if ((local variable) int ii % 64 == 0)
{
msg.xclient.data.l[0] = 42; // event traffic for the main pump
XSendEvent(dpy, win, False, 0, &msg);
}
}
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("thread=worker", "action=done roundtrips=%d", (local variable) int ii * 2);
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites '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.
atomicStore((shared global) shared(bool) app.g_workerDoneg_workerDone, true);
msg.xclient.data.l[0] = 99; // wake the main pump out of XNextEvent
XSendEvent(dpy, win, False, 0, &msg);
XFlush(dpy);
return null;
}
/// XGetGeometry needs 9 out-params; tuck them away.
void app.XGetGeometry_XGetGeometry needs 9 out-params; tuck them away.
XGetGeometry_(Display* (parameter) Display* dpydpy, Window (parameter) Window winwin) @nogc nothrow
{
Window _error_ rootroot;
int _error_ xx, _error_ yy;
uint _error_ ww, _error_ hh, _error_ bwbw, _error_ depthdepth;
XGetGeometry(dpy, win, &root, &x, &y, &w, &h, &bw, &depth);
}
int int app.probeWindowOnWorker(bool initFirst, bool initLate) nothrow @nogcprobeWindowOnWorker(bool (parameter) bool initFirstinitFirst, bool (parameter) bool initLateinitLate) @nogc nothrow
{
if ((parameter) bool initFirstinitFirst)
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XInitThreads ret=%d order=first", XInitThreads());
g_dpy = XOpenDisplay(null);
if ((parameter) bool initLateinitLate)
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XInitThreads ret=%d order=AFTER_XOpenDisplay", XInitThreads());
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XOpenDisplay fd=%d xinitthreads=%s",
XConnectionNumber(g_dpy),
(parameter) bool initFirstinitFirst ? "first".(constant) immutable(char)* "first".ptr = "first"ptr : (parameter) bool initLateinitLate ? "late".(constant) immutable(char)* "late".ptr = "late"ptr : "no".(constant) immutable(char)* "no".ptr = "no"ptr);
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites '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.
atomicStore((shared global) shared(bool) app.g_workerDoneg_workerDone, false);
pthread_t (local variable) ulong tt;
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong tt, null, &void* app.windowWorker(void* __param_0) nothrow @nogcWorker
create/map a window, then hammer reply-carrying requests
(XInternAtom round-trips) for ~1.2 s. Without XInitThreads both threads
read the one connection -> interleaved replies -> corruption.
windowWorker, null);
// Main pumps events while issuing its own (buffered) requests — i.e. a
// normal GUI main loop — on the same unlocked Display the worker is
// hammering. XNoOp appends to the output buffer; XPending flushes it and
// reads events; both race the worker's marshalling and replies.
int (local variable) int eventsOnMaineventsOnMain = 0;
bool (local variable) bool sawDoneWakeupsawDoneWakeup = false;
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("thread=main action=pump_start");
while (!(local variable) bool sawDoneWakeupsawDoneWakeup && !(bool core.atomic.atomicLoad!(MemoryOrder.seq, bool)(ref return scope shared(const(bool)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(bool) app.g_workerDoneg_workerDone) && XPending(g_dpy) == 0))
{
XNoOp(g_dpy); // buffered request from the main thread
while (XPending(g_dpy) > 0)
{
XEvent (local variable) _error_ evev;
XNextEvent(g_dpy, &ev);
++(local variable) int eventsOnMaineventsOnMain;
if (ev.type == (enum value) app.MapNotify = 19MapNotify)
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("thread=main", "action=event type=MapNotify window=0x%lx events_on_main=%d",
ev.xmap.window, (local variable) int eventsOnMaineventsOnMain);
if (ev.type == (enum value) app.ClientMessage = 33ClientMessage && ev.xclient.data.l[0] == 99)
(local variable) bool sawDoneWakeupsawDoneWakeup = true; // the worker's done-wakeup
}
}
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong tt, null);
XSync(g_dpy, False);
static __gshared char[192] (__gshared global) char[192] app.probeWindowOnWorker.dd;
const (local variable) const(int) errserrs = int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_xlibErrorsg_xlibErrors);
if ((local variable) const(int) errserrs > 0)
{
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr, (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) ulong char[192].length = 192LUlength, "xlib_errors=%d last=%s events_on_main=%d",
(local variable) const(int) errserrs, (__gshared global) char[128] app.g_lastXlibErrorg_lastXlibError.(constant) char* char[128].ptr = &g_lastXlibErrorptr, (local variable) int eventsOnMaineventsOnMain);
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("error", (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr);
}
else if ((parameter) bool initFirstinitFirst)
{
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr, (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) ulong char[192].length = 192LUlength, "window_created_on_worker events_on_main=%d", (local variable) int eventsOnMaineventsOnMain);
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("ok", (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr);
}
else
{
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr, (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) ulong char[192].length = 192LUlength,
"no_corruption_observed_this_run events_on_main=%d (nondeterministic)", (local variable) int eventsOnMaineventsOnMain);
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("silent", (__gshared global) char[192] app.probeWindowOnWorker.dd.(constant) char* char[192].ptr = &dptr);
}
XCloseDisplay(g_dpy);
return 0;
}
// -- probe 3: two threads blocked in XNextEvent on one Display ------------------
private shared int (shared global) shared(int) app.g_recvCountg_recvCount;
private shared int[2] (shared global) shared(int[2]) app.g_perThreadg_perThread;
private shared int (shared global) shared(int) app.g_waitersAliveg_waitersAlive;
extern (C) void* void* app.eventWaiter(void* arg) nothrow @nogceventWaiter(void* (parameter) void* argarg) @nogc nothrow
{
const (local variable) const(int) idxidx = cast(int) cast((alias) object.size_t = ulongsize_t) (parameter) void* argarg;
int core.atomic.atomicOp!("+=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"+="((shared global) shared(int) app.g_waitersAliveg_waitersAlive, 1);
for (;;)
{
XEvent (local variable) _error_ evev;
XNextEvent(g_dpy, &ev); // blocking; the lock is released while waiting
if (ev.type != (enum value) app.ClientMessage = 33ClientMessage)
continue;
if (ev.xclient.data.l[0] == 99) // sentinel: drain and exit
break;
int core.atomic.atomicOp!("+=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"+="((shared global) shared(int[2]) app.g_perThreadg_perThread[(local variable) const(int) idxidx], 1);
int core.atomic.atomicOp!("+=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"+="((shared global) shared(int) app.g_recvCountg_recvCount, 1);
}
int core.atomic.atomicOp!("-=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"-="((shared global) shared(int) app.g_waitersAliveg_waitersAlive, 1);
return null;
}
int int app.probeTwoReaders() nothrow @nogcprobeTwoReaders() @nogc nothrow
{
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XInitThreads ret=%d order=first", XInitThreads());
g_dpy = XOpenDisplay(null);
Window (local variable) _error_ winwin = XCreateSimpleWindow(g_dpy, XRootWindow(g_dpy, XDefaultScreen(g_dpy)),
0, 0, 100, 100, 0, 0, 0);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=window_created xid=0x%lx", win);
pthread_t[2] (local variable) ulong[2] tsts;
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong[2] tsts[0], null, &void* app.eventWaiter(void* arg) nothrow @nogceventWaiter, cast(void*) 0);
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong[2] tsts[1], null, &void* app.eventWaiter(void* arg) nothrow @nogceventWaiter, cast(void*) 1);
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep(50_000); // let both block inside XNextEvent
enum (constant) int app.probeTwoReaders.N = 100N = 100;
XEvent (local variable) _error_ evev;
ev.xclient.type = ClientMessage;
ev.xclient.window = win;
ev.xclient.message_type = XInternAtom(g_dpy, "WSI_F17_MSG", False);
ev.xclient.format = 32;
foreach ((local variable) int ii; 0 .. (constant) int app.probeTwoReaders.N = 100N)
{
ev.xclient.data.l[0] = 42;
XSendEvent(g_dpy, win, False, 0, &ev); // self-addressed event
XFlush(g_dpy);
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep(1000);
}
// Wake the (possibly both-blocked) waiters out of XNextEvent.
int (local variable) int sentinelssentinels = 0;
while (int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_waitersAliveg_waitersAlive) > 0 && (local variable) int sentinelssentinels < 200)
{
ev.xclient.data.l[0] = 99;
XSendEvent(g_dpy, win, False, 0, &ev);
XFlush(g_dpy);
++(local variable) int sentinelssentinels;
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep(5000);
}
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong[2] tsts[0], null);
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong[2] tsts[1], null);
static __gshared char[160] (__gshared global) char[160] app.probeTwoReaders.dd;
const (local variable) const(int) t0t0 = int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int[2]) app.g_perThreadg_perThread[0]), (local variable) const(int) t1t1 = int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int[2]) app.g_perThreadg_perThread[1]);
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[160] app.probeTwoReaders.dd.(constant) char* char[160].ptr = &dptr, (__gshared global) char[160] app.probeTwoReaders.dd.(constant) ulong char[160].length = 160LUlength,
"sent=%d received=%d thread_a=%d thread_b=%d sentinels_to_unblock=%d xlib_errors=%d",
(constant) int app.probeTwoReaders.N = 100N, (local variable) const(int) t0t0 + (local variable) const(int) t1t1, (local variable) const(int) t0t0, (local variable) const(int) t1t1, (local variable) int sentinelssentinels, int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_xlibErrorsg_xlibErrors));
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict((local variable) const(int) t0t0 + (local variable) const(int) t1t1 == (constant) int app.probeTwoReaders.N = 100N ? "ok" : "error", (__gshared global) char[160] app.probeTwoReaders.dd.(constant) char* char[160].ptr = &dptr);
XCloseDisplay(g_dpy);
return 0;
}
// -- probe 4: XShmPutImage from a render thread, events pumped on main ----------
private __gshared Window _error_ app.g_wing_win;
private __gshared GC _error_ app.g_gcg_gc;
private __gshared XImage* _error_ app.g_ximgg_ximg;
private shared int (shared global) shared(int) app.g_putsg_puts;
extern (C) void* void* app.renderWorker(void* __param_0) nothrow @nogcrenderWorker(void*) @nogc nothrow
{
foreach ((local variable) int frameframe; 0 .. 60)
{
void* core.stdc.string.memset(return scope void* s, int c, ulong n) pure nothrow @nogcmemset(g_ximg.data, (local variable) int frameframe * 4, cast((alias) object.size_t = ulongsize_t)(g_ximg.bytes_per_line * g_ximg.height));
XShmPutImage(g_dpy, g_win, g_gc, g_ximg, 0, 0, 0, 0,
cast(uint) g_ximg.width, cast(uint) g_ximg.height, True);
XFlush(g_dpy); // the worker owns its own flushes
int core.atomic.atomicOp!("+=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"+="((shared global) shared(int) app.g_putsg_puts, 1);
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep(3000); // paced by time, not by completion (main eats those)
}
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("thread=render action=done puts=60");
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites '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.
atomicStore((shared global) shared(bool) app.g_workerDoneg_workerDone, true);
return null;
}
int int app.probeRenderThread() nothrow @nogcprobeRenderThread() @nogc nothrow
{
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=XInitThreads ret=%d order=first", XInitThreads());
g_dpy = XOpenDisplay(null);
const (local variable) const(_error_) screenscreen = XDefaultScreen(g_dpy);
if (!XShmQueryExtension(g_dpy))
{
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("ok", "skipped_no_MIT_SHM");
return 0;
}
const (local variable) const(_error_) completionTypecompletionType = XShmGetEventBase(g_dpy) + 0 /* ShmCompletion */ ;
g_win = XCreateSimpleWindow(g_dpy, XRootWindow(g_dpy, screen), 0, 0,
320, 240, 1, 0, 0xffffff);
XSelectInput(g_dpy, g_win, ExposureMask | StructureNotifyMask);
XMapWindow(g_dpy, g_win);
g_gc = XDefaultGC(g_dpy, screen);
XShmSegmentInfo (local variable) _error_ shminfoshminfo;
g_ximg = XShmCreateImage(g_dpy, XDefaultVisual(g_dpy, screen),
cast(uint) XDefaultDepth(g_dpy, screen), ZPixmap, null, &shminfo, 320, 240);
shminfo.shmid = shmget(IPC_PRIVATE,
cast((unresolved type) size_tsize_t)(g_ximg.bytes_per_line * g_ximg.height), IPC_CREAT | 0x180);
shminfo.shmaddr = cast(char*) shmat(shminfo.shmid, null, 0);
g_ximg.data = shminfo.shmaddr;
shminfo.readOnly = False;
XShmAttach(g_dpy, &shminfo);
XSync(g_dpy, False);
shmctl(shminfo.shmid, IPC_RMID, null);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("step", "name=shm_image size=320x240 completion_event=%d", completionType);
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites '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.
atomicStore((shared global) shared(bool) app.g_workerDoneg_workerDone, false);
pthread_t (local variable) ulong tt;
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong tt, null, &void* app.renderWorker(void* __param_0) nothrow @nogcrenderWorker, null);
int (local variable) int completionscompletions = 0, (local variable) int otherEventsotherEvents = 0;
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("thread=main action=pump_start");
const (local variable) const(long) deadlinedeadline = long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() + 4_000_000;
while (long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() < (local variable) const(long) deadlinedeadline)
{
while (XPending(g_dpy) > 0)
{
XEvent (local variable) _error_ evev;
XNextEvent(g_dpy, &ev);
if (ev.type == completionType)
++(local variable) int completionscompletions;
else
++(local variable) int otherEventsotherEvents;
}
if (bool core.atomic.atomicLoad!(MemoryOrder.seq, bool)(ref return scope shared(const(bool)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(bool) app.g_workerDoneg_workerDone) && (local variable) int completionscompletions >= int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_putsg_puts))
break;
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trustedusleep(1000);
}
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong tt, null);
static __gshared char[160] (__gshared global) char[160] app.probeRenderThread.dd;
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[160] app.probeRenderThread.dd.(constant) char* char[160].ptr = &dptr, (__gshared global) char[160] app.probeRenderThread.dd.(constant) ulong char[160].length = 160LUlength,
"puts_from_render_thread=%d completions_on_main=%d other_events_on_main=%d xlib_errors=%d",
int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_putsg_puts), (local variable) int completionscompletions, (local variable) int otherEventsotherEvents, int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_xlibErrorsg_xlibErrors));
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict((local variable) int completionscompletions == int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_putsg_puts) && int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_xlibErrorsg_xlibErrors) == 0
? "ok" : "error", (__gshared global) char[160] app.probeRenderThread.dd.(constant) char* char[160].ptr = &dptr);
XSync(g_dpy, False);
XShmDetach(g_dpy, &shminfo);
g_ximg.f.destroy_image(g_ximg);
shmdt(shminfo.shmaddr);
XCloseDisplay(g_dpy);
return 0;
}
// -- probe 5: one Display per thread, no XInitThreads ---------------------------
private shared int (shared global) shared(int) app.g_threadOkg_threadOk;
extern (C) void* void* app.displayPerThread(void* arg) nothrow @nogcdisplayPerThread(void* (parameter) void* argarg) @nogc nothrow
{
const (local variable) const(int) idxidx = cast(int) cast((alias) object.size_t = ulongsize_t) (parameter) void* argarg;
Display* (local variable) _error_ dpydpy = XOpenDisplay(null); // private connection: nothing shared
const (local variable) const(_error_) screenscreen = XDefaultScreen(dpy);
Window (local variable) _error_ winwin = XCreateSimpleWindow(dpy, XRootWindow(dpy, screen), 0, 0,
160, 120, 1, 0, 0xffffff);
XSelectInput(dpy, win, ExposureMask | StructureNotifyMask);
XMapWindow(dpy, win);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("thread=t%d", "action=window_created xid=0x%lx fd=%d",
(local variable) const(int) idxidx, win, XConnectionNumber(dpy));
bool (local variable) bool exposedexposed = false;
const (local variable) const(long) deadlinedeadline = long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() + 3_000_000;
while (!(local variable) bool exposedexposed && long instrument.nowUs() nothrow @nogcMicroseconds elapsed since initInstrument.
nowUs() < (local variable) const(long) deadlinedeadline)
{
XEvent (local variable) _error_ evev;
XNextEvent(dpy, &ev); // each thread blocks on ITS OWN connection
if (ev.type == (enum value) app.Expose = 12Expose)
{
XFillRectangle(dpy, win, XDefaultGC(dpy, screen), 10, 10, 140, 100);
XSync(dpy, False);
(local variable) bool exposedexposed = true;
}
}
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("thread=t%d", "action=done exposed=%d", (local variable) const(int) idxidx, cast(int) (local variable) bool exposedexposed);
if ((local variable) bool exposedexposed)
int core.atomic.atomicOp!("+=", int, int)(ref shared(int) val, int mod) pure nothrow @nogc @safePerforms the binary operation 'op' on val using 'mod' as the modifier.
atomicOp!"+="((shared global) shared(int) app.g_threadOkg_threadOk, 1);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
return null;
}
int int app.probeDisplayPerThread() nothrow @nogcprobeDisplayPerThread() @nogc nothrow
{
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("step name=XInitThreads SKIPPED_ON_PURPOSE model=display_per_thread");
pthread_t[2] (local variable) ulong[2] tsts;
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong[2] tsts[0], null, &void* app.displayPerThread(void* arg) nothrow @nogcdisplayPerThread, cast(void*) 0);
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogcpthread_create(&(local variable) ulong[2] tsts[1], null, &void* app.displayPerThread(void* arg) nothrow @nogcdisplayPerThread, cast(void*) 1);
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong[2] tsts[0], null);
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogcpthread_join((local variable) ulong[2] tsts[1], null);
static __gshared char[96] (__gshared global) char[96] app.probeDisplayPerThread.dd;
int core.stdc.stdio.snprintf(scope char* s, ulong n, scope const(char*) format, scope const ...) nothrow @nogcsnprintf((__gshared global) char[96] app.probeDisplayPerThread.dd.(constant) char* char[96].ptr = &dptr, (__gshared global) char[96] app.probeDisplayPerThread.dd.(constant) ulong char[96].length = 96LUlength, "threads_completed=%d/2 xlib_errors=%d no_xinitthreads=1",
int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_threadOkg_threadOk), int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_xlibErrorsg_xlibErrors));
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict(int core.atomic.atomicLoad!(MemoryOrder.seq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trustedLoads '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.
atomicLoad((shared global) shared(int) app.g_threadOkg_threadOk) == 2 ? "ok" : "error", (__gshared global) char[96] app.probeDisplayPerThread.dd.(constant) char* char[96].ptr = &dptr);
return 0;
}
// ---------------------------------------------------------------------------
int int app.runProbe(int n) nothrow @nogcrunProbe(int (parameter) int nn) @nogc nothrow
{
(__gshared global) int app.g_probeg_probe = (parameter) int nn;
void app.installCrashHandlers() nothrow @nogcinstallCrashHandlers();
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("probe_start", "n=%d", (parameter) int nn);
switch ((parameter) int nn)
{
case 1:
int app.probeWindowOnWorker(bool initFirst, bool initLate) nothrow @nogcprobeWindowOnWorker(false, false);
break;
case 2:
int app.probeWindowOnWorker(bool initFirst, bool initLate) nothrow @nogcprobeWindowOnWorker(true, false);
break;
case 3:
int app.probeTwoReaders() nothrow @nogcprobeTwoReaders();
break;
case 4:
int app.probeRenderThread() nothrow @nogcprobeRenderThread();
break;
case 5:
int app.probeDisplayPerThread() nothrow @nogcprobeDisplayPerThread();
break;
case 6:
int app.probeWindowOnWorker(bool initFirst, bool initLate) nothrow @nogcprobeWindowOnWorker(false, true);
break;
default:
void app.verdict(scope const(char)* result, scope const(char)* detail) nothrow @nogcThe one line the spec requires per run. Async-signal-safe on purpose
(snprintf into a static buffer + write(2)) so the signal/XIO handlers can
call it; the normal path uses it too so the format is identical.
verdict("error", "unknown_probe");
}
uint core.sys.posix.unistd.alarm(uint) nothrow @nogc @trustedalarm(0);
return 0;
}
int int D main(string[] args)main((alias) object.string = stringstring[] (parameter) string[] argsargs)
{
void instrument.initInstrument(const(char)* demoName) nothrow @nogcNames the demo, starts the monotonic clock, and emits init_start.
Call this first, before any platform API call.
initInstrument("f17_x11");
int (local variable) int probeprobe = 0;
foreach ((parameter) string aa; (parameter) string[] argsargs[1 .. $])
if ((local variable) string aa.(field) ulong string.lengthlength > 8 && (local variable) string aa[0 .. 8] == "--probe=")
(local variable) int probeprobe = (local variable) string aa[8] - '0';
// Capability gate in the parent, before any fork.
Display* (local variable) _error_ testtest = XOpenDisplay(null);
if (test is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: no X11 display (XOpenDisplay returned null)\n");
return 0;
}
XCloseDisplay(test);
if ((local variable) int probeprobe != 0)
return int app.runProbe(int n) nothrow @nogcrunProbe((local variable) int probeprobe);
// No argument: run the full matrix, each probe TWICE (the F17 spec's
// nondeterminism rule), each in a forked child so a corrupted Xlib or a
// caught crash never poisons the next probe. Children always _exit(0).
foreach ((local variable) int nn; 1 .. 7)
foreach ((local variable) int runrun; 1 .. 3)
{
const (local variable) const(int) pidpid = int core.sys.posix.unistd.fork() nothrow @nogc @trustedfork();
if ((local variable) const(int) pidpid == 0)
{
int app.runProbe(int n) nothrow @nogcrunProbe((local variable) int nn);
noreturn core.sys.posix.unistd._exit(int) nothrow @nogc @trusted_exit(0);
}
int (local variable) int statusstatus;
int core.sys.posix.sys.wait.waitpid(int, int*, int) nothrow @nogcwaitpid((local variable) const(int) pidpid, &(local variable) int statusstatus, 0);
void instrument.emitf(scope const(char)* kind, scope const(char)* fmt, ...) nothrow @nogcEmit an event with a printf-formatted key=value ... payload.
emitf("probe_child", "n=%d run=%d wait_status=%d", (local variable) int nn, (local variable) int runrun, (local variable) int statusstatus);
}
void instrument.emit(scope const(char)* kind) nothrow @nogcEmit an event with no key=value payload.
emit("teardown all_probes_done");
return 0;
}