Notes regarding a design dialog with Claude
This commit is contained in:
parent
dc66af56c1
commit
e0d4de5737
1 changed files with 962 additions and 0 deletions
|
|
@ -901,6 +901,968 @@
|
||||||
<node CREATED="1783644399671" ID="ID_1684157516" MODIFIED="1783644406037" TEXT="Submission-Handler"/>
|
<node CREATED="1783644399671" ID="ID_1684157516" MODIFIED="1783644406037" TEXT="Submission-Handler"/>
|
||||||
<node CREATED="1783644407569" ID="ID_69520880" MODIFIED="1783644412668" TEXT="Display-Handler"/>
|
<node CREATED="1783644407569" ID="ID_69520880" MODIFIED="1783644412668" TEXT="Display-Handler"/>
|
||||||
</node>
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783900894906" ID="ID_1398236830" MODIFIED="1783900901714" TEXT="Design-Fragen">
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783900916807" ID="ID_1965380217" LINK="https://claude.ai/share/2d09d5e6-1a47-45e9-b05b-500550fb0977" MODIFIED="1783964756096" STYLE="fork" TEXT="Design / Technologie-Dialog mit Claude">
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
<node CREATED="1783900973837" ID="ID_271684795" MODIFIED="1783900988031" TEXT="Claude weist nach meiner Einführung sofort auf Knackpunkte hin">
|
||||||
|
<node CREATED="1783900994827" ID="ID_39866859" MODIFIED="1783901005106" TEXT="user-space buffer or preregistered buffers?"/>
|
||||||
|
<node CREATED="1783901008546" ID="ID_336576874" MODIFIED="1783901018571" TEXT="depth of pipeline: real pipelining?"/>
|
||||||
|
<node CREATED="1783901023762" ID="ID_561342572" MODIFIED="1783901034901" TEXT="waiting / timing strategy in the worker thread"/>
|
||||||
|
<node CREATED="1783901038197" ID="ID_8240786" MODIFIED="1783901059421" TEXT="File IO : use O_DIRECT or IORING_OP_READ"/>
|
||||||
|
<node CREATED="1783901079926" ID="ID_1856365027" MODIFIED="1783901085930" TEXT="Threading ownership model"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783901091272" ID="ID_504921767" MODIFIED="1783901102000" TEXT="ich kläre die (mir ist die Entscheidung schon klar)">
|
||||||
|
<node CREATED="1783901105001" ID="ID_406126832" MODIFIED="1783901129762" TEXT="den Talk so einfach wie möglich halten">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
die Zuhörer sind keine Systemprogrammierer
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783901133887" ID="ID_1114781881" MODIFIED="1783901146536" TEXT="daher die einfachen userspace-Buffer und das normale read()"/>
|
||||||
|
<node CREATED="1783901151199" ID="ID_168398984" MODIFIED="1783901166527" TEXT="selbstverständlich will ich eine echte pipeline, d.h. mehrere Buffer">
|
||||||
|
<icon BUILTIN="ksmiletris"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783901170599" ID="ID_1292606828" MODIFIED="1783901198318" TEXT="timing/waiting sowie Ownership/Handover ist was ich in der Diskussion klären möchte">
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783901673639" ID="ID_1158267991" MODIFIED="1783901688375" TEXT="ich frage nach Timing-Sync mit Video-Output">
|
||||||
|
<node CREATED="1783901711226" ID="ID_1930870094" MODIFIED="1783901723228" TEXT="Claude schlägt vor, überhaupt nicht zu »synchronisieren«"/>
|
||||||
|
<node CREATED="1783901727663" ID="ID_1596599158" MODIFIED="1783901972168" TEXT="Argument: da gibts nichts zu synchronisieren, denn die Sync geht nicht auf">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Der Desktop läuft mit z.B. 60fps, das video mit 25fps. Man sollte akzeptieren (und im Talk klar machen) daß das niemals aufgeht. Vielmehr sollte man regelmäßig die fertig geladenen Frames übergeben, wenn sie eben da sind. Für den Talk schlägt Claude vor, das Thema "tearing" mehr oder weniger zu ignorieren (hatte ich nicht gesagt, sehe ich aber genauso). Später, für die Prod-Version sei das ein nicht-trivialer Aspekt
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783901847104" ID="ID_1936627376" MODIFIED="1783901856567" TEXT="Guter Punkt, da hätte ich mich möglicherweise verrant!">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783901974887" ID="ID_1974536015" MODIFIED="1783902062028" TEXT="Claude geht davon aus, daß die OpenGL-Pipeline im GUI-Thread laufen sollte">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...das sei zwar nicht notwendig (korrekt!), aber vermeide Komplexitäten. Ein OpenGL-Kontext ist an einen Thread gebunden, also müßte man nach jedem Thread-Wechsel umbinden.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
⟹ sehr gut, damit hat sich eine Frage von selber geklärt, die ich noch stellen wollte
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902186522" ID="ID_80755144" MODIFIED="1783902207310" TEXT="wegen dem Timing weist Claude auf die Möglichkeit hin, das über io_uring selber zu machen">
|
||||||
|
<icon BUILTIN="back"/>
|
||||||
|
<node CREATED="1783902209900" ID="ID_168498311" MODIFIED="1783902217323" TEXT="wow ... das hatte ich gar nicht auf dem Schrim"/>
|
||||||
|
<node CREATED="1783902217878" ID="ID_118182743" MODIFIED="1783902223322" TEXT="das wäre natürlich super elegant"/>
|
||||||
|
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1783902223899" ID="ID_1596372987" MODIFIED="1783902235325" TEXT="allerdings dann definitiv nicht was wir in Lumiera machen werden">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902236744" ID="ID_1546055488" MODIFIED="1783902253577">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
allerdings: der Worker wäre damit ein <b>Proactor</b>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902258661" ID="ID_67058752" MODIFIED="1783902369689" TEXT="wollte ich zwar erst nicht, könnte mich aber mit dem Gedanken anfreunden">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
....es wäre nämlich tatsächlich dann ganz ideomatisch (so hab ich die Doku verstanden, und Claude sagt das von sich aus auch).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Zwar hatte ich die Idee, im Talk schon grundsätzlich etwas zu machen, was funktioniert wie in Lumiera geplant. Aber wenn ich mir das <i>wirklich vom didaktischen Gesichtspunkt her nochmal überlege,</i> ist es nicht so geschickt.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902501677" ID="ID_742016664" MODIFIED="1783902513392" TEXT="Claude fragt genauer nach der GTK-Integration">
|
||||||
|
<node CREATED="1783902514414" ID="ID_1855924638" MODIFIED="1783902527158" TEXT="ich gebe Kontext: "Gtk the toolkit, not the framework""/>
|
||||||
|
<node CREATED="1783902529522" ID="ID_117691046" MODIFIED="1783902544351" TEXT="erkläre wie der Demo-Code vom letzten Jahr grundsätzlich funktioniert"/>
|
||||||
|
<node CREATED="1783902544813" ID="ID_265030509" MODIFIED="1783902552490" TEXT="Claude meint, das vereinfache das Design"/>
|
||||||
|
<node CREATED="1783902552987" ID="ID_1533807493" MODIFIED="1783902569992" TEXT="er schlägt vor: nur einen »Tick« verwenden, und zwar io_uring"/>
|
||||||
|
<node CREATED="1783902573851" ID="ID_451286400" MODIFIED="1783902590704" TEXT="wenn ein Frame ready ist, sollte man lediglich dem GUI-Thread signalisieren"/>
|
||||||
|
<node CREATED="1783902591217" ID="ID_1763048000" MODIFIED="1783902681362" TEXT="er schlägt dafür g_idle_add() vor -- das sei low-level GLib und weithin etabliert">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...es würde zur "toolkit, nicht framework"-Policy passen, und zudem wäre das Signal explizit von jedem Thread aus triggerbar
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902821732" ID="ID_1465201307" MODIFIED="1783902838122" TEXT="getriggert würde dann der Code, der die OpenGL-Pipeline mit neuen Daten versorgt"/>
|
||||||
|
<node CREATED="1783902838536" ID="ID_1259653595" MODIFIED="1783902845946" TEXT="dieser würde mithin im GTK-Thread laufen"/>
|
||||||
|
<node CREATED="1783902847135" ID="ID_1990712475" MODIFIED="1783902883778" TEXT="Hinweis auf die VSync-Problematik">
|
||||||
|
<arrowlink COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="154;0;" ID="Arrow_ID_707364611" STARTARROW="None" STARTINCLINATION="140;12;"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#efdeb8" CREATED="1783956967738" ID="ID_1170137190" MODIFIED="1783957012593" TEXT="Wichtige Einsicht: warum nicht jeder Desktop-Frame per Buffer-Flip bedient werden muß">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
<node CREATED="1783957017373" ID="ID_1419210108" MODIFIED="1783957033385" TEXT="wenn wir neue daten haben dann triggern wir die Anzige-Pipeline"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957040816" ID="ID_468719143" MODIFIED="1783957188965" STYLE="fork" TEXT="das funktioniert weil darunter ein Compositor sitzt">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
body { font-family: SansSerif; font-size: 12pt }
|
||||||
|
p { margin-top: 0 }
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<ul type="text/css">
|
||||||
|
<li>
|
||||||
|
As an aside: do I assume correct that the OpenGL side will just redisplay the same data if nothing is sent for intermittent buffer flips (assuming that the desktop frame rate is higher than the video frame rate)?
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong http-equiv="content-type" content="text/html; charset=utf-8">Your aside — yes, and here's the actual mechanism:</strong> with swap interval 0 and swaps only triggered by <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">frameReady.emit()</code>, you are <em>not</em> relying on OpenGL to "resend" anything between your swaps — OpenGL/EGL has no ongoing activity at all in between. What actually keeps the picture on screen is the desktop compositor: once <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglSwapBuffers()</code> makes a buffer the current front buffer, that's simply what's there until you swap again, and the compositor samples it at its own repaint cadence (its vsync, typically 60Hz+) regardless of whether your app does anything. So your assumption holds, but the credit belongs to "a swapped buffer is persistent state the compositor keeps re-showing," not to any active redisplay behavior on your side — a good one-sentence clarification to have ready if someone in the audience asks "wait, doesn't something have to keep pushing frames?"
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783957160551" ID="ID_1093301164" MODIFIED="1783957181327" TEXT="OpenGL macht gar nichts, wenn kein swapBuffers() aufgerufen wird">
|
||||||
|
<icon BUILTIN="ksmiletris"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783904677906" ID="ID_923117300" MODIFIED="1783904701552" TEXT="habe Claude dann die URL für unseren Demo-Code gegeben">
|
||||||
|
<node CREATED="1783904704085" ID="ID_1817354735" MODIFIED="1783904712017" TEXT="bestätigt die vorherige Diskussion"/>
|
||||||
|
<node CREATED="1783904712480" ID="ID_471788469" MODIFIED="1783904750809" TEXT="Klarstellung: solte Gtkmm verwenden"/>
|
||||||
|
<node CREATED="1783904918332" ID="ID_788373194" MODIFIED="1783904930062" TEXT="Claude bestätigt: der Umbau der GtkApp dürfte trivial sein"/>
|
||||||
|
<node CREATED="1783904930773" ID="ID_1456697138" MODIFIED="1783904945590" TEXT="er schlägt vor: ich sollte auch den onFrame-Callback so erhalten, nur anders aufrufen"/>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#520f69" CREATED="1783904751803" ID="ID_648202546" MODIFIED="1783904801052" TEXT="lasse mir von Claude zwei Code-Templates generieren">
|
||||||
|
<icon BUILTIN="edit"/>
|
||||||
|
<node CREATED="1783904765875" ID="ID_1730135150" MODIFIED="1783904774302" TEXT="die C-Lösung mit g_idle_add()"/>
|
||||||
|
<node CREATED="1783904774961" ID="ID_795180608" MODIFIED="1783904783468" TEXT="die Gtkmm-Lösung mit dem Glib::Dispatcher"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783952838505" ID="ID_1257919936" MODIFIED="1783952858029" TEXT="Worker Thread and State organisation">
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783952862448" ID="ID_842498915" MODIFIED="1783954578059" STYLE="fork">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
ich habe das Stichwort "<b>Proactor</b>" gebraucht. Claude bestätigt das
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
das heißt: die Lösung, auf die es nun für den Talk hinausläuft, ist daß ich nicht wirklich einen "Worker" hab (kein Vorgriff auf die Lumiera Engine), sondern einen Proactor.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783954584980" ID="ID_532077726" MODIFIED="1783954609626" TEXT="⟹ heißt: blocken auf wait_cqe()">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783953153918" ID="ID_1754327495" MODIFIED="1783953520369" TEXT="ich sage: für async IO braucht man immer eine Art State-Engine">
|
||||||
|
<node CREATED="1783953183906" ID="ID_339096490" MODIFIED="1783953350863" TEXT="Claude widerspricht nicht">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Ich verwende das aktuelle "Sonnet" auf Stufe Medium. Das braucht erhebliche Rechenzeit für jede Antwort auf diesem Diskussionslevel (und zeigt auch jeweils expandierende, schlußfolgernde und kondensierende Phasen an)
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783953200905" ID="ID_1912082579" MODIFIED="1783953252578" TEXT="aber er faßt die von mir genannten Optionen zusammen in zwei Klassen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>Proactor, confirmed.</strong> io_uring is the textbook proactor: you submit a description of the completed operation you want, the kernel does the work, and your thread's job is purely to consume results and act on them — as opposed to a reactor (epoll-style), where you're told "this fd is ready," and <em>you</em> still perform the (potentially blocking, now guaranteed-non-blocking) I/O yourself. Good instinct to name this explicitly in the talk; it's the single sentence that makes the rest of the design legible.
|
||||||
|
</p>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>On the "abstraction of state progression" claim</strong> — I think it's correct, but I'd collapse your four options into two families rather than four separate things:
|
||||||
|
</p>
|
||||||
|
<ul class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1 [&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-disc flex flex-col gap-1 pl-8 mb-3">
|
||||||
|
<li class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
<strong>Implicit state, paid for in threads.</strong> Thread-per-operation blocking I/O doesn't <em>avoid</em> needing a state machine — it hides one inside the OS thread: the thread's stack and program counter <em>are</em>  the state ("I am here, at this line, holding these locals"). You get state-tracking for free, but you pay for it in thread/stack cost per concurrent operation, which is exactly what proactor-style async I/O exists to avoid.
|
||||||
|
</li>
|
||||||
|
<li class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
<strong>Explicit state, paid for in bookkeeping.</strong> Everything else — hand-rolled flags, continuation chains, and coroutines — is the <em>same</em>  idea at different levels of automation. A coroutine is, mechanically, a compiler-synthesized state machine: <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">co_await</code> points become case labels, locals that need to survive a suspension become member fields of a generated struct. Continuation-passing is the same thing done by hand with closures instead of compiler codegen. So "coroutines" and "explicit state engine" aren't really two different answers to your question — a coroutine <em>is</em> an explicit state engine, just one you didn't have to write by hand.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
Given that framing: you're right that a flag-per-buffer is not a lesser or simplified version of "the real solution" — it's the same solution as coroutines, just written manually because your problem is small and linear enough that the compiler-generated version would be pure overhead (both in runtime terms and, more importantly here, in <em>audience cognitive load</em> — introducing C++20 coroutine machinery to explain a 3-state pipeline would be a bad trade for this talk).
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783953439647" ID="ID_478135892" MODIFIED="1783953440909" TEXT="enum class SlotState { Empty, Submitted, Ready };"/>
|
||||||
|
<node CREATED="1783953460141" ID="ID_1859865790" MODIFIED="1783954625972">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
std::<b>atomic</b><SlotState>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783953478348" ID="ID_186490339" MODIFIED="1783954023236" STYLE="fork" TEXT="with release-on-write / acquire-on-read">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Since each slot has exactly one producer (the worker) and one consumer (the GUI thread, via the dispatcher callback) and they never touch a slot at the same time by construction,
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">std::atomic<SlotState></code> with release-on-write / acquire-on-read is enough — no mutex needed. That pairing is worth a beat in the talk on its own: the atomic isn't there to prevent concurrent writes (there aren't any), it's there to guarantee the GUI thread sees the <em>buffer contents</em> the worker wrote, not just the state flag — a clean, concrete illustration of why acquire/release exists, tied directly to something the audience just watched you build.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783953378671" ID="ID_1599260447" MODIFIED="1783953395622" TEXT="Claude sagt, man sollte dann jeden Buffer auch mit der Frame-Nummer markieren"/>
|
||||||
|
<node CREATED="1783953396149" ID="ID_12931299" MODIFIED="1783953416430" TEXT="ich sage: das bekommt man doch von selbst, wenn man die Buffer in einen Ring organisiert"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783953941213" ID="ID_713186135" MODIFIED="1783954028109" STYLE="fork" TEXT="Claude greift das auf, betont aber (nochmal) daß die CQ-Reihenfolge nicht garantiert ist">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
The ring idea is correct, and it's the right way to lay out storage — but it solves a different problem than the one we flagged last time, and I think that's the thing worth pulling apart carefully, because the two compose nicely rather than one replacing the other.
|
||||||
|
</p>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>What the cyclic layout gives you:</strong> a deterministic mapping from frame number to physical buffer — <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">slot = frameNumber % N</code> — so you never need a free-list, an allocator, or any "find me an empty slot" search. You always know exactly where frame <em>K</em>'s data belongs before you even submit the read. That's a clean, standard pattern and exactly right for a fixed pool of reusable buffers.
|
||||||
|
</p>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>What it does <em>not</em> give you for free:</strong> the display-order guarantee. The ring tells you <em>where</em> frame K's bytes live; it says nothing about <em>when</em> the read for frame K finishes relative to the read for frame K+1. Two SQEs submitted in order are not guaranteed to complete in order — for sequential reads against one local file on one machine it's very likely in practice, but "likely" is exactly the kind of unverified pragmatism you just said you don't accept. So you still need the mechanism from last time: track which frame is <em>next expected for display</em>, and only <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">emit()</code> once that specific slot says <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Ready</code>  — regardless of what else has completed in the meantime.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783954062803" ID="ID_281008431" MODIFIED="1783954065743" TEXT="weitere Hinweise">
|
||||||
|
<node CREATED="1783954070297" ID="ID_1690793152" MODIFIED="1783954092918" TEXT="man sollte die frameNr als "user data" verwenden">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
<node CREATED="1783954096287" ID="ID_1210379052" MODIFIED="1783954102642" TEXT="da wäre ich nicht draufgekommen"/>
|
||||||
|
<node CREATED="1783954103307" ID="ID_399338998" MODIFIED="1783954150362" TEXT="trotzdem würde ich die übliche Praxis mit dem Pointer darstellen"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783954171957" ID="ID_1248856525" MODIFIED="1783954243633" STYLE="fork" TEXT="sollte einen "nextExpected"-Cursor im Thread-State mitführen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
Keep one cursor, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">nextExpected</code> (monotonically increasing, starts at 0). On every completion, mark <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">slots[frameNumber % N]</code> as <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Ready</code>. Separately — and this can literally be a small loop, since bursts of completions can land between two checks — while <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">slots[nextExpected % N].state == Ready</code>: emit the dispatcher for that slot, mark it <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Empty</code>  again, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">++nextExpected</code>.
|
||||||
|
</li>
|
||||||
|
<li class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
The same <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">nextExpected</code>-style counter also tells the <em>submission</em>  side what to do: whenever a slot frees up (goes back to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Empty</code>  after display), submit a new read for <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">nextToSubmit</code>, into <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">slots[nextToSubmit % N]</code>, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">++nextToSubmit</code>. Priming at startup is just: submit frames <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">0..N-1</code> into slots <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">0..N-1</code> before the first <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">wait_cqe</code>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783954256289" ID="ID_582576620" MODIFIED="1783954281086" TEXT="»Backpressure« falles out for free with this design"/>
|
||||||
|
<node CREATED="1783954286225" ID="ID_681083402" MODIFIED="1783954360400" TEXT="sollte nextFrame und Frame-Pos im Quellfile konzeptionell trennen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
...obschon man sie auf dem gleichen lokalen State aufbaut. Das Beispiel, das Claude nennt: man könnte das Quellfile loopen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783956243726" ID="ID_1521391070" MODIFIED="1783956255868" TEXT="Scheduling und Behandeln der Aktionen via IO_URING">
|
||||||
|
<node CREATED="1783956270352" ID="ID_1579558384" MODIFIED="1783957478739" TEXT="zeige an daß ich liburing verwenden möchte">
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783956516487" ID="ID_1971249448" MODIFIED="1783956541623" TEXT="Claude schreibt mir drei Code-Snippets">
|
||||||
|
<node CREATED="1783956542891" ID="ID_922011151" MODIFIED="1783956927430" TEXT="Setup von Uring und Timer + initiales Befüllen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
struct io_uring ring;
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
// One reserved tag that can never collide with a real frame number:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
constexpr uint64_t TICK_TAG = UINT64_MAX;
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
// The periodic beat — submitted ONCE, keeps firing on its own:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
struct __kernel_timespec ts = { .tv_sec = 0, .tv_nsec = 40'000'000 }; // 40ms ≈ 25fps
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
struct io_uring_sqe* tsqe = io_uring_get_sqe(&ring);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
io_uring_prep_timeout(tsqe, &ts, 0, IORING_TIMEOUT_MULTISHOT);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
io_uring_sqe_set_data64(tsqe, TICK_TAG);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
// Prime the pipeline — one read per slot, tagged with its frame number:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
for (uint64_t f = 0; f < N; ++f) {
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    io_uring_prep_read(sqe, fd, slots[f % N].buf, FRAME_BYTES, f * FRAME_BYTES);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    io_uring_sqe_set_data64(sqe, f);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
io_uring_submit(&ring);   // one syscall flushes everything prepped above
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783956564734" ID="ID_519769608" MODIFIED="1783956917195" TEXT="Skelett der Proactor-Loop">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
for (;;) {
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    struct io_uring_cqe* cqe;
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    io_uring_wait_cqe(&ring, &cqe);      // blocks — the worker's only wait point
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    uint64_t tag = cqe->user_data;
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    if (tag == TICK_TAG)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
        onTick();
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    else
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
        onReadComplete(/*frame=*/tag, cqe->res);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    io_uring_cqe_seen(&ring, cqe);       // MUST always call this, or the CQ ring fills and stalls
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783956594083" ID="ID_1956714359" MODIFIED="1783956898511" TEXT="onTick + follow-up">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
void onTick() { if (slots[nextExpected % N].state == Ready) { frameReady.emit(); // GUI thread will draw slots[nextExpected % N] ++nextExpected; // GUI marks the slot Empty once it's done drawing } // else: do nothing — this frame slips one tick later. Not dropped, just delayed. while (slots[nextToSubmit % N].state == Empty) { submitRead(nextToSubmit); // io_uring_prep_read + set_data64(nextToSubmit) ++nextToSubmit; } io_uring_submit(&ring); }
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<node CREATED="1783956613435" ID="ID_121998302" MODIFIED="1783956875196" TEXT="Claude erläutert dazu....">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
The reason top-up belongs in <code>onTick</code> rather than in <code>onReadComplete</code>:<br />a slot only becomes reusable once the <em>GUI thread</em> has finished drawing it and flips it back to <code>Empty</code>  — and the worker, sitting blocked in <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">io_uring_wait_cqe</code>, has no way to know that happened except by waking up for <em>something</em>. The tick is that guaranteed periodic wake-up, so it doubles as "check if there's new capacity to fill," with a worst-case latency of one tick period (~40ms) before a freed slot gets reused — fine at this cadence, and a nice illustration that the timer isn't just pacing display, it's also the thing that keeps the whole state machine alive even when nothing else is happening.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1783957589661" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!--
|
||||||
|
body { font-family: SansSerif; font-size: 12pt }
|
||||||
|
p { margin-top: 0 }
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<ul type="text/css">
|
||||||
|
<li>
|
||||||
|
can you write me a short inline function that takes the "40 millisecods" as a std::chrono spec ("40ms") and returns the struct __kernel_timespec
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
#include <chrono>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
#include <linux/time_types.h>   // struct __kernel_timespec (also pulled in via liburing.h)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
template<class Rep, class Period>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
constexpr struct __kernel_timespec
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
toKernelTimespec(std::chrono::duration<Rep, Period> d)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    using namespace std::chrono;
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    auto secs  = duration_cast<seconds>(d);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    auto nsecs = duration_cast<nanoseconds>(d - secs);
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
    return __kernel_timespec{ static_cast<long long>(secs.count()),
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
                               static_cast<long long>(nsecs.count()) };
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<font color="#800c0c">auto ts = toKernelTimespec(40ms);</font>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e9c7a3" COLOR="#440099" CREATED="1783964845540" ID="ID_784995591" MODIFIED="1783964898453" TEXT="schließe eine allgemeine Überlegung an btr. Lumiera-Engine">
|
||||||
|
<icon BUILTIN="back"/>
|
||||||
|
<node CREATED="1783964913819" ID="ID_1290458999" MODIFIED="1783964940263" TEXT="gebe Claude eine »executive summary« bezüglich Lumiera Engine">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
er hat dazu keine weiteren Fragen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#338800" CREATED="1783964942767" ID="ID_993102807" MODIFIED="1783966105865" TEXT="lasse Claude die Diskrepanz herausarbeiten gegenüber dem Proactor-Pattern">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
bestätigt meine Analyse, ohne Diskrepanz
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783966114659" ID="ID_325758280" MODIFIED="1783966124441" TEXT="Claude identifiziert Problemzonen">
|
||||||
|
<node CREATED="1783966132732" ID="ID_227446892" MODIFIED="1783966134051" TEXT=""someone has to drain the CQ""/>
|
||||||
|
<node CREATED="1783966148026" ID="ID_1364400629" MODIFIED="1783966149873" TEXT="what guarantees the CQ actually gets drained in a timely fashion?"/>
|
||||||
|
<node CREATED="1783966160726" ID="ID_1852031512" MODIFIED="1783966162572" TEXT="who owns the ring at engine scale?"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783966216653" ID="ID_62921124" MODIFIED="1783966234722" TEXT="ich frame das Problem mit den lang laufenden Trasks als »Backpressure«">
|
||||||
|
<node CREATED="1783966235635" ID="ID_492766487" MODIFIED="1783966241017" TEXT="Claude akzeptiert das"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783966254735" ID="ID_1661464656" MODIFIED="1783966306039" STYLE="fork" TEXT="aber er nennt das "implicit backpressure">
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783966276865" ID="ID_1822059325" MODIFIED="1783966298429" STYLE="fork" TEXT="bemerkt: es ist offen ob das genügt">
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783966316107" ID="ID_1390907760" MODIFIED="1783966366559" TEXT="ich geme mein Reasoning bezüglich Ring-Ownership">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
As far as I could see from looking at the liburing sources, there is a fundamental read barrier before retrieving/updating the rings, and a write barrier at the other side (when done with a completion or submitting). Now you also pointed out the conclusion that submitting the next IO task could itself be a scheduled activity. Combining both, if we ensure that interaction with io_uring happens only in the "mutual exclusive" phase (i.e. the worker holds the GroomingToken) -- then that would ensure that, at any given time, only one thread at max interacts with the urings. And Liburing inserts the necessary barriers, so that the next task to enter that zone would see the changes. Does that reasoning hold? What difficulties do you see?
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783966388626" ID="ID_857234213" MODIFIED="1783966398679" STYLE="fork" TEXT="yeour reasoning holds, with one caveat that matters a lot and a couple of smaller ones.">
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#4a0f69" CREATED="1783966412590" ID="ID_1533910419" MODIFIED="1783966530095" TEXT="Achtung: acquire / release - Semantik is notwendig auf dem Grooming-Token">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Ja, weiß ich. Deshalb hab ich's so programmiert. Freut mich daß Claude da unabhängig zum gleichen Schluß kommt (denn ich hab mich oft gefragt, ob ich mich da in Spitzfindigkeiten verrenne).<br />Also nochmal explizit: memory_order_relaxed reicht hier nicht<br />
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783966538813" ID="ID_340512988" MODIFIED="1783966733292" TEXT="das GroomingToken und die Barrieren in liburing dienen verschiedenen Zwecken">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Einmal geht es um shared-data zwischen Kernel und Userspace. Wohingegen das Grooming-Token die Sichtbarkeit über die Worker hinweg steuert.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Diese Unterscheidung ist konzeptionell (sehe ich so, Claude deutet es auch so). Beide Ansätze stören sich nicht, man sollte sie aber auseinanderhalten. Ich denke, das ist ein generischer guter Rat beim Design solcher Systeme, man sollte nicht danach streben, solche verschiedenen Belange zusammenzufassen, selbst wenn es im konkreten Fall möglich wäre (wie hier der Fall, aber nur weil keine data-dependenciy im memory-ordering includiert ist)
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1783966752311" ID="ID_1363514128" MODIFIED="1783966837345" TEXT="whatever you do inside the token-held section must never block! ">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
That's a much bigger problem than in the FrOSCon demo, where blocking on
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">wait_cqe</code> was the worker's <em>entire job</em>. Here it would reintroduce exactly the kind of global stall your pull architecture is designed to avoid. The fix is straightforward but needs to be deliberate: only ever use the non-blocking <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">io_uring_peek_cqe()</code>  inside grooming — check if anything's ready, harvest what's there, submit what's ready to submit, and move on regardless of whether anything was found. That keeps the ring interaction cheap and bounded, consistent with a critical section that's already supposed to be short.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="clanbomber"/>
|
||||||
|
<node CREATED="1783966769728" ID="ID_882108638" MODIFIED="1783966784791" TEXT="Gefahr: wait_cqe() kann die ganze Engine stallen"/>
|
||||||
|
<node CREATED="1783966793306" ID="ID_1685734854" MODIFIED="1783966801007" TEXT="stets nur io_uring_peek_cqe() verwenden">
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783966849036" ID="ID_145214537" MODIFIED="1783966861508" TEXT="an Completion-Queue-Overflow denken">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783966894277" ID="ID_231061601" MODIFIED="1783966904895" STYLE="fork" TEXT="">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
CQ overflow: if grooming genuinely doesn't run for a while under heavy CPU load, does a full completion queue drop completions, or buffer them? Recent kernels buffer overflow internally rather than dropping (subject to
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">IORING_FEATURE_NODROP</code>-era behavior), but it's worth confirming for whatever kernel baseline you commit to, rather than assuming.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783966925433" ID="ID_852283957" MODIFIED="1783967012774">
|
||||||
|
<richcontent TYPE="NODE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Deliberately avoid <code><font color="#7c5252">IORING_SETUP_SQPOLL</font></code>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="clanbomber"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967014652" ID="ID_758622095" MODIFIED="1783967890660" STYLE="fork" TEXT="">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Deliberately avoid
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">IORING_SETUP_SQPOLL</code>. It's tempting since it removes the need to call into the kernel for submission most of the time, but it works by adding a kernel-side polling thread that reads the SQ ring on its own schedule — an actor outside your GroomingToken discipline and outside your capacity self-calibration entirely. That's exactly the kind of uncontrolled independent actor your architecture is built to avoid.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<linktarget COLOR="#4389dd" DESTINATION="ID_758622095" ENDARROW="Default" ENDINCLINATION="196;89;" ID="Arrow_ID_200171107" SOURCE="ID_156133270" STARTARROW="None" STARTINCLINATION="334;27;"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967051428" ID="ID_1173652481" MODIFIED="1783967077582" STYLE="fork" TEXT="Claude gibt eine »freundliche conclusion«">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Net: I think folding I/O into the same single-threaded grooming discipline you already have is the
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<em http-equiv="content-type" content="text/html; charset=utf-8">consistent</em> choice, not a compromise — you've already accepted one global serialization point for scheduling decisions, and a small, non-blocking peek/submit pair is cheap enough to ride along in that same section without becoming a new bottleneck category. The real design work is less "is this safe" (I think it is, modulo the ordering-semantics check and the non-blocking discipline) and more "how do we know, empirically, that grooming-section latency stays small enough once I/O bookkeeping is added" — which sounds like exactly the kind of thing you'd want numbers for before committing, rather than reasoning about further in the abstract.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
<node CREATED="1783967080199" ID="ID_820048763" MODIFIED="1783967099409" TEXT="geschickt schmeicheln wurde gut trainiert">
|
||||||
|
<icon BUILTIN="smiley-oh"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783967101185" ID="ID_88930827" MODIFIED="1783967123407" TEXT="gut verpackte Warnung: nicht wehter gehen ohne empirsche Daten">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783967137487" ID="ID_551703511" MODIFIED="1783967158497" TEXT="bitte Claude, dieses Desidng in zeitgenössische Trends einzuordnen">
|
||||||
|
<node CREATED="1783967167257" ID="ID_714798259" MODIFIED="1783967181600" TEXT="er sieht das als Konvergenz in zwei Trends"/>
|
||||||
|
<node CREATED="1783967182462" ID="ID_592074869" MODIFIED="1783967198604" TEXT="das Scheduler / Work-stealing Design wird sogar standardisiert"/>
|
||||||
|
<node CREATED="1783967246522" ID="ID_550800786" MODIFIED="1783967291305" TEXT="der zeweite, gegensätzliche Trend sei "thread-per-core no shared data"">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967583375" ID="ID_1425666844" MODIFIED="1783967597761" STYLE="fork" TEXT="">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
<strong http-equiv="content-type" content="text/html; charset=utf-8">Current 2 — thread-per-core, shared-nothing, and it's picking up real steam.</strong> This is the more consequential trend to be aware of, because it's a genuinely different answer to the same problem you're solving, and it's currently the fashionable one in high-performance I/O-heavy systems: pin one thread per core, partition data and work by that core (typically via hashing), eliminate cross-thread shared state entirely, and communicate exclusively via message passing rather than shared queues or locks. This is the architecture behind ScyllaDB and Redpanda via the Seastar framework, and it's spreading into the Rust ecosystem too (Glommio, compio, and others). A recent writeup from the Apache Iggy project frames the shift precisely as moving from work stealing to "work steering" — i.e., instead of idle workers reaching into a shared structure (what you have), work is routed to the specific core that already owns the relevant data, and nobody has to contend for anything. Interestingly, the standard advice against a single shared io_uring instance per application is exactly the contention concern we discussed for your ring — each thread in Glommio gets its own private set of rings instead, which is the thread-per-core answer to the same "who owns the ring" question you and I just spent a while on. <a href="https://iggy.apache.org/blogs/2026/02/27/thread-per-core-io_uring/" target="_blank" class="group/tag relative h-[18px] rounded-full inline-flex items-center overflow-hidden -translate-y-px cursor-pointer">Apache + 2</a>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783967332866" ID="ID_655858860" MODIFIED="1783967346236" TEXT=""from work stealing to work steering""/>
|
||||||
|
<node CREATED="1783967350704" ID="ID_650693098" MODIFIED="1783967359922" TEXT="relevant for high-performance computing"/>
|
||||||
|
<node CREATED="1783967361702" ID="ID_1030181773" MODIFIED="1783967366938" TEXT="nanosecons statt milliseconds"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783967397582" ID="ID_980790835" MODIFIED="1783967420293" TEXT="Diverse Quellenangaben im Chatverlauf">
|
||||||
|
<icon BUILTIN="back"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783967604917" ID="ID_1465824912" MODIFIED="1783967742187" STYLE="fork" TEXT="abschließene Einordnung">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Even more directly relevant to your capacity-calibration idea: Glommio ships built-in "controllers" that automatically adjust per-task-queue scheduler shares to control throughput against a user-specified criterion — that's essentially your self-calibrating capacity-weights idea, independently arrived at by a different team solving an adjacent problem. Worth knowing that's not an idiosyncratic idea of yours — it's something the field has landed on more than once, which is generally a good sign for an architectural instinct.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://docs.rs/glommio/latest/glommio/" target="_blank">Rust</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>Where I'd place your design relative to these two currents:</strong> your GroomingToken-serialized shared-scheduler model sits closer to "classic" worker-pool architecture than to shared-nothing thread-per-core — and I think that's the <em>right</em> call for Lumiera specifically, not a lag behind the state of the art. The strongest argument for thread-per-core is when per-request overhead is so small (network request handling, KV-store lookups — sub-microsecond territory) that even brief lock contention or cache-line bouncing dominates the cost. Your own numbers — single-digit milliseconds up into seconds per frame task — are three to six orders of magnitude coarser than that regime. A brief critical section for grooming and ring bookkeeping is genuinely negligible next to task costs measured in milliseconds; it would not be negligible next to costs measured in tens of nanoseconds, which is the world Seastar/ScyllaDB actually live in. So I wouldn't read "thread-per-core is trendy" as pressure to abandon your model — I'd read it as: the technique exists and is well-proven if your task granularity ever drops by several orders of magnitude (which, for a video render engine, seems unlikely — your bottleneck is decode/filter/encode work, not tiny message dispatch).
|
||||||
|
</p>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>One trend worth just keeping an eye on, not acting on:</strong> core counts keep climbing — high core-count workstation and consumer chips are increasingly normal — and any single serialization point gets proportionally more expensive as contention scales with thread count, even if each individual crossing is cheap. Not a problem today, and your task granularity buys you a lot of headroom, but it's the kind of thing worth having empirical data on (exactly the "more empirics needed" conclusion you already reached) if Lumiera is ever deployed on genuinely large core counts rather than typical desktop/workstation counts.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||||
|
<icon BUILTIN="wizard"/>
|
||||||
|
<node CREATED="1783967750406" ID="ID_1287889954" MODIFIED="1783967801786" TEXT="Hinweis auf steigende Anzahl an Cores">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Lumiera könnte ja schon auch auf eine high-end-Workstation laufen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783967758928" ID="ID_284611592" MODIFIED="1783967785981" TEXT="sollte Contention um das single-threaded bottleneck im Blick behalten">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783901529929" ID="ID_1631713573" MODIFIED="1783901543887" TEXT="nebenbei notiert">
|
||||||
|
<icon BUILTIN="bell"/>
|
||||||
|
<node COLOR="#435e98" CREATED="1783901558710" ID="ID_156133270" MODIFIED="1783967976008" STYLE="fork" TEXT="was ist IORING_SETUP_SQPOLL?">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
von Claude erwähnt im Zusammenhang mit Locking visibility. Er weist auch darauf hin daß io_uring keine multiple thread submissions unterstützt. Zu dem Schluß war ich ja auch gekommen
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<arrowlink COLOR="#4389dd" DESTINATION="ID_758622095" ENDARROW="Default" ENDINCLINATION="196;89;" ID="Arrow_ID_200171107" STARTARROW="None" STARTINCLINATION="334;27;"/>
|
||||||
|
<font NAME="SansSerif" SIZE="12"/>
|
||||||
|
<icon BUILTIN="help"/>
|
||||||
|
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1783967899141" HGAP="34" ID="ID_1537697930" MODIFIED="1783967981234" TEXT="verstanden — klarer Fall" VSHIFT="23">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
der Kernel kann nicht zaubern; stattdessen hat er dann einfach einen poll-Worker. Keine gute Idee in einer Architektur die selber alle Resourcen steuern möchte
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="ksmiletris"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783902798597" ID="ID_599725786" MODIFIED="1783902800007" TEXT="eglSwapInterval">
|
||||||
|
<node CREATED="1783902800007" ID="ID_1393441883" MODIFIED="1783902877712" TEXT="damit kann man wohl das Verhlätnis zu VSync steuern">
|
||||||
|
<linktarget COLOR="#347acc" DESTINATION="ID_1393441883" ENDARROW="Default" ENDINCLINATION="154;0;" ID="Arrow_ID_707364611" SOURCE="ID_1990712475" STARTARROW="None" STARTINCLINATION="140;12;"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783904509744" ID="ID_1126395932" MODIFIED="1783904513706" TEXT="X11 / Wayland">
|
||||||
|
<node CREATED="1783904515877" ID="ID_718620596" MODIFIED="1783904524313" TEXT="habe das nebenbei erwähnt"/>
|
||||||
|
<node CREATED="1783904524844" ID="ID_473487490" MODIFIED="1783904606751" TEXT="Claude bestätigt: unter EGL ist die Anpassung nur minimal">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p http-equiv="content-type" content="text/html; charset=utf-8" class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
<strong>Wayland/X11</strong>
|
||||||
|
</p>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
Your instinct is basically right, but I'd sharpen "not too deep" a bit: the EGL calls that matter for your loop — <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglCreateContext</code>, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglMakeCurrent</code>, <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglSwapBuffers</code> — are identical either way. What differs is only the step just before <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">eglCreateWindowSurface</code>, i.e. how you obtain the native window handle from GDK:
|
||||||
|
</p>
|
||||||
|
<ul class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1 [&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-disc flex flex-col gap-1 pl-8 mb-3">
|
||||||
|
<li class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
X11: you already have this — <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">gdk_x11_window_get_xid()</code> gives you a raw <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Window</code> XID directly usable as the native window type.
|
||||||
|
</li>
|
||||||
|
<li class="font-claude-response-body whitespace-normal break-words pl-2">
|
||||||
|
Wayland: GDK gives you a <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">wl_surface*</code> (<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">gdk_wayland_window_get_wl_surface()</code>), but EGL on Wayland doesn't accept that directly — you have to wrap it with <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">wl_egl_window_create()</code> (from <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">libwayland-egl</code>) to get something usable as <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">EGLNativeWindowType</code>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="font-claude-response-body break-words whitespace-normal">
|
||||||
|
So it's genuinely a small, well-isolated patch of platform glue — a handful of lines, gated behind a runtime check of which GDK backend is active (<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">GDK_IS_X11_DISPLAY</code> / <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">GDK_IS_WAYLAND_DISPLAY</code>) — everything downstream of surface creation is untouched.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#435e98" CREATED="1783968013173" ID="ID_1777944229" MODIFIED="1783968109171" TEXT="Fazit">
|
||||||
|
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||||
|
<node CREATED="1783968016550" ID="ID_1512987627" MODIFIED="1783968023075" TEXT="ich baue nun doch einen Proaktor"/>
|
||||||
|
<node CREATED="1783968025925" ID="ID_109357172" MODIFIED="1783968034071" TEXT="verwende Glib::Dispatcher für hand-over"/>
|
||||||
|
<node CREATED="1783968039719" ID="ID_1431893413" MODIFIED="1783968050997" TEXT="für die Daten-Sichtbarkeit genügt eine atomic-state-flag"/>
|
||||||
|
<node CREATED="1783968058381" ID="ID_234180982" MODIFIED="1783968077778" TEXT="IO_URING hat selber einen Timing-Mechanismus ⟹ den sollte man nutzen"/>
|
||||||
|
<node CREATED="1783968083676" ID="ID_1583854744" MODIFIED="1783968100714" TEXT="Buffer zyklisch organisieren. Next-Frame-Nr als Key verwenden"/>
|
||||||
|
<node BACKGROUND_COLOR="#fefc4e" COLOR="#351d75" CREATED="1783968124839" ID="ID_32919071" MODIFIED="1783968137925" TEXT="Allgemeine Einsichten für die Lumiera-Engine">
|
||||||
|
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
<node CREATED="1783968139661" ID="ID_721741944" MODIFIED="1783968157925" TEXT="Proactor ist push — unsere Architektur ist konsistent pull"/>
|
||||||
|
<node COLOR="#5b280f" CREATED="1783968173638" ID="ID_1363800541" MODIFIED="1783968193807" TEXT="man sollte nicht versuchen, einen Proactor in unser Design zu integrieren">
|
||||||
|
<icon BUILTIN="stop-sign"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968195246" ID="ID_1729925953" MODIFIED="1783968212467" TEXT="stattdessen kann man die Proactor-Loop in Activities oder Jobs packen"/>
|
||||||
|
<node CREATED="1783968233008" ID="ID_1599145728" MODIFIED="1783968259313" TEXT="Auf Engine-Level: Zugang zum URing muß in der »Grooming-Zone« bleiben">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968264384" ID="ID_1390930082" MODIFIED="1783968317234" TEXT="das ist ausreichend um die Cross-Thread-Visibility sicherzustellen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
aber man sollte konzeptionell zwischen der visibility mit dem Kernel und der Visibility zwischen den Workern unterscheiden; für ersteres gibt es die Barrieren in Liburing
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783968339226" ID="ID_52764530" MODIFIED="1783968361355" TEXT="Blocken in der Grooming-Phase verhindern ⟶ sonst Engine-Stall">
|
||||||
|
<icon BUILTIN="clanbomber"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968381176" ID="ID_291465589" MODIFIED="1783968485845" TEXT="Flow/Capacity-Steuerung ist essentiell notwendig">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
der Umstand daß neue IO-Aktivitäten gescheduled werden und Completions entnommen werden müssen, garantiert nur eine passive Backpressure (nicht entnommene Completions gehen nicht verloren, und der Platz in den URings ist begrenzt). Sehr wahrscheinlich ist mehr notwendig, damit die Engine rund läuft.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968535936" ID="ID_1390321079" MODIFIED="1783968546673" TEXT="Einordnung in gegenwärtige Trends">
|
||||||
|
<node CREATED="1783968547646" ID="ID_1203574703" MODIFIED="1783968635282" TEXT="Scheduler/Worker-Pattern wird standardisiert (C++26)">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Es ist eine etablierte und bewährte Methode, und wird nicht weggehen. Was wir geschaffen haben konvergiert gut mit den allgemeinen Einsichten im Feld
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968562254" ID="ID_1154684301" MODIFIED="1783968600700" TEXT="High-Performance-Computing: thread-per-core no shared data">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
das ist ein Gegentrend, der aber auf einer anderen Größenordnung arbeitet (im Nanosekunden-Bereich)
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</richcontent>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1783968636663" ID="ID_1048046164" MODIFIED="1783968651582" TEXT="sollte an sehr hohe Anzahl an Cores denken">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
<node CREATED="1783644425567" ID="ID_238189215" MODIFIED="1783644429274" TEXT="Flow-Steuerung">
|
<node CREATED="1783644425567" ID="ID_238189215" MODIFIED="1783644429274" TEXT="Flow-Steuerung">
|
||||||
<node CREATED="1783644430659" ID="ID_269686877" MODIFIED="1783644452555" TEXT="Aufrabe: Vorlauf steuern">
|
<node CREATED="1783644430659" ID="ID_269686877" MODIFIED="1783644452555" TEXT="Aufrabe: Vorlauf steuern">
|
||||||
<node CREATED="1783644529049" ID="ID_486151338" MODIFIED="1783644875737" TEXT="zwei Phasen (Chase/Lock)">
|
<node CREATED="1783644529049" ID="ID_486151338" MODIFIED="1783644875737" TEXT="zwei Phasen (Chase/Lock)">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue