Prototype: code improvements

- rename the states to better reflect their purpose
 - introduce getters for in BufferDispatcher
This commit is contained in:
Fischlurch 2026-07-16 19:31:34 +02:00
parent 0cb388d4a7
commit c9f9fec117
3 changed files with 176 additions and 152 deletions

View file

@ -369,16 +369,13 @@
</node>
<node CREATED="1784162963010" ID="ID_1477358179" MODIFIED="1784163021269" TEXT="oder direkt die negierte ERRNO">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
IO_URING kann errno nicht sinnvoll direkt verwenden (errno ist eine globale variable)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -843,11 +840,12 @@
</node>
<node CREATED="1779504863066" ID="ID_986008832" MODIFIED="1779504921775" TEXT="jeden Frame in den Output-Code aus dem Video-output-Vortrag schicken"/>
<node CREATED="1783642371760" ID="ID_622863035" MODIFIED="1783642377238" TEXT="Konstruktions-Schema">
<node CREATED="1783642378650" ID="ID_460419" MODIFIED="1783642391427" TEXT="naheliegend: einen Proactor zeigen">
<node CREATED="1783642378650" ID="ID_460419" MODIFIED="1784222786307" TEXT="naheliegend: einen Proactor zeigen">
<node CREATED="1783642938966" ID="ID_1117409154" MODIFIED="1783642955536" TEXT="m&#xe4;&#xdf;ig sinnvoll"/>
<node CREATED="1783642956739" ID="ID_141690825" MODIFIED="1783642968326" TEXT="hier nur ein Consumer"/>
<node CREATED="1784222792149" ID="ID_1259331721" MODIFIED="1784222808639" TEXT="ist aber die Standard-L&#xf6;sung (push)"/>
</node>
<node COLOR="#5b280f" CREATED="1783642985618" ID="ID_169416260" MODIFIED="1784052474215" TEXT="oder einen pull-Worker">
<node COLOR="#5b280f" CREATED="1783642985618" FOLDED="true" ID="ID_169416260" MODIFIED="1784052474215" TEXT="oder einen pull-Worker">
<icon BUILTIN="button_cancel"/>
<node CREATED="1783642995707" ID="ID_216666777" MODIFIED="1783642999794" TEXT="das w&#xe4;re n&#xe4;her an Lumiera"/>
<node CREATED="1783643000727" ID="ID_737342556" MODIFIED="1783643655114" TEXT="der w&#xfc;rde jeweils nur die Queue ausr&#xe4;umen"/>
@ -894,8 +892,8 @@
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1783644313430" ID="ID_170028474" MODIFIED="1783788829437" TEXT="Prototyp entwickeln">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1783644336145" ID="ID_413309976" MODIFIED="1783788834125" TEXT="Skelett">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1783644336145" ID="ID_413309976" MODIFIED="1784222754616" TEXT="Skelett">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1783788677873" ID="ID_551733396" LINK="#ID_517750346" MODIFIED="1783788805152" TEXT="Lizenz: GPL v2+"/>
<node COLOR="#338800" CREATED="1783644339455" ID="ID_932453573" MODIFIED="1783788820590" TEXT="CMake">
<icon BUILTIN="button_ok"/>
@ -944,13 +942,24 @@
<node COLOR="#338800" CREATED="1783644357376" ID="ID_1687423764" MODIFIED="1784142889926" TEXT="ein Proactor-Thread">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1783644382004" ID="ID_222096554" MODIFIED="1783644392847" TEXT="Handler">
<node CREATED="1783644394013" ID="ID_599467746" MODIFIED="1783644398783" TEXT="Completion-Handler"/>
<node CREATED="1783644399671" ID="ID_1684157516" MODIFIED="1783644406037" TEXT="Submission-Handler"/>
<node CREATED="1783644407569" ID="ID_69520880" MODIFIED="1783644412668" TEXT="Display-Handler"/>
<node COLOR="#338800" CREATED="1783644382004" ID="ID_222096554" MODIFIED="1784222739934" TEXT="Handler">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1783644394013" ID="ID_599467746" MODIFIED="1784222741419" TEXT="Completion-Handler">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1783644399671" ID="ID_1684157516" MODIFIED="1784222743125" TEXT="Submission-Handler">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1783644407569" ID="ID_69520880" MODIFIED="1784222744755" TEXT="Display-Handler">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1784222746115" ID="ID_1070022489" MODIFIED="1784222750547" TEXT="Shutdown-Handler">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1783900894906" ID="ID_1398236830" MODIFIED="1783900901714" TEXT="Design-Fragen">
</node>
<node COLOR="#234872" CREATED="1783900894906" ID="ID_1398236830" MODIFIED="1784222832373" TEXT="Design-Fragen">
<icon BUILTIN="yes"/>
<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"/>
@ -1411,9 +1420,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783956594083" ID="ID_1956714359" MODIFIED="1784136509799" STYLE="fork" TEXT="onTick + follow-up">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<pre>void onTick()
&#160;&#160;{
@ -1434,8 +1441,7 @@
</pre>
</body>
</html>
</richcontent>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<icon BUILTIN="edit"/>
@ -1863,7 +1869,8 @@
</node>
</node>
</node>
<node CREATED="1783984352027" ID="ID_1602724940" MODIFIED="1783984359484" TEXT="Aufbau der Application-Shell">
<node BACKGROUND_COLOR="#b2e9d4" COLOR="#014e21" CREATED="1783984352027" ID="ID_1602724940" MODIFIED="1784222687829" TEXT="Aufbau der Application-Shell">
<icon BUILTIN="yes"/>
<node CREATED="1783984371031" ID="ID_1219649133" MODIFIED="1783984401222" TEXT="das Grundschema von letztem Jahr war n&#xfc;tzlich">
<node CREATED="1783984407459" ID="ID_321798557" MODIFIED="1783984427656" TEXT="&#x201e;mu&#xdf; den GTK-Code gar nicht anschauen&#x201c;"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1783985102373" ID="ID_734300736" MODIFIED="1784076612700" TEXT="Aber: die Ablaufsteuerung liegt jetzt nicht mehr (komplett) in GTK">
@ -1951,7 +1958,9 @@
<node CREATED="1783986866318" ID="ID_623423104" MODIFIED="1783986874503" TEXT="alles per Konstruktor-Verdrahtung"/>
</node>
</node>
<node CREATED="1784076499094" ID="ID_1786219220" MODIFIED="1784076516947" TEXT="Player per Konstruktur-Initialisierung">
<node COLOR="#1f267f" CREATED="1784076499094" ID="ID_1786219220" MODIFIED="1784222712766" TEXT="Player per Konstruktur-Initialisierung">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="back"/>
<node CREATED="1784076561463" ID="ID_1248489844" MODIFIED="1784076582686" TEXT="l&#xe4;uft im GUI-Thread">
<icon BUILTIN="idea"/>
</node>
@ -1962,6 +1971,7 @@
<node CREATED="1784076675289" ID="ID_1648662317" MODIFIED="1784076685507" TEXT="unmittelbar hier den Thread starten"/>
</node>
</node>
</node>
<node CREATED="1783644425567" ID="ID_238189215" MODIFIED="1783644429274" TEXT="Flow-Steuerung">
<node CREATED="1784052955974" ID="ID_1491751742" MODIFIED="1784052978769" TEXT="da nun Proactor &#x27f9; getrieben vom Timer-Tick">
<icon BUILTIN="idea"/>
@ -1975,9 +1985,7 @@
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#fefc4e" COLOR="#351d75" CREATED="1784076979882" ID="ID_1843330900" MODIFIED="1784078228911" TEXT="Bemerkenswert: das hat sich durch die Konversation so ergeben">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...es war nicht so, da&#223; Claude diese &#8222;Idee gehabt&#8220; h&#228;tte &#8212; denn ich hatte die Konversation ja so gef&#252;hrt, da&#223; Claude mir meine Konzept-Ans&#228;tze validiert und verfeinert. Dann habe ich das Prinzip formuliert, da&#223; f&#252;r Async-IO irgendwo eine State-Machine sein mu&#223;. Claude hat das aufgegriffen, validiert und versch&#228;rft; und damit war offensichtlich geworden, da&#223; man diese State-Flag als Atomic auslegen kann. Das habe ich in dem Moment genauso &#187;offensichtlich&#171; gesehen, und Claude hat es ebenso formuliert.
@ -1997,8 +2005,7 @@
&#10233; das wirft ein Licht darauf, wie dieses <i>scheinbar intelligente &#187;Generieren&#171; funktioniert, </i>auf Basis einer industriellen Daten-Technik, die Plausibilit&#228;ten integriert und interpoliert.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="back"/>
</node>
@ -2006,27 +2013,24 @@
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="info"/>
<node CREATED="1784078717834" ID="ID_17978925" MODIFIED="1784078954065" TEXT="IO beauftragen: FREE &#x27f6; READ"/>
<node CREATED="1784078958419" ID="ID_1531715805" MODIFIED="1784078974343" TEXT="&#xdc;bergabe an Display: READ &#x27f6; SHOW"/>
<node CREATED="1784165852364" ID="ID_721661485" MODIFIED="1784167391018" TEXT="Warten auf Display: SHOW &#x27f6; WAIT">
<node CREATED="1784078958419" ID="ID_1531715805" MODIFIED="1784221555857" TEXT="&#xdc;bergabe an Display: READ &#x27f6; FEED"/>
<node CREATED="1784165852364" ID="ID_721661485" MODIFIED="1784221545799" TEXT="Warten auf Display: FEED &#x27f6; SHOW">
<linktarget COLOR="#6574c5" DESTINATION="ID_721661485" ENDARROW="Default" ENDINCLINATION="925;77;" ID="Arrow_ID_86014624" SOURCE="ID_312946304" STARTARROW="None" STARTINCLINATION="397;-23;"/>
</node>
<node CREATED="1784078981634" ID="ID_1578413583" MODIFIED="1784167275599" TEXT="Frame-Flip: WAOT &#x27f6; FREE"/>
<node CREATED="1784078981634" ID="ID_1578413583" MODIFIED="1784221551696" TEXT="Frame-Flip: SHOW &#x27f6; FREE"/>
<node CREATED="1784165852364" ID="ID_849967229" MODIFIED="1784167346009" TEXT="Abbrunch/End-of-File: markiert durch STOP">
<linktarget COLOR="#6574c5" DESTINATION="ID_849967229" ENDARROW="Default" ENDINCLINATION="755;51;" ID="Arrow_ID_1693130420" SOURCE="ID_948621808" STARTARROW="None" STARTINCLINATION="411;-22;"/>
</node>
</node>
<node CREATED="1784078466234" ID="ID_424139171" MODIFIED="1784078496653">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
hier mu&#223; auch die <b>Backpressure</b>&#160;beachtet und realisiert werden
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1784078502730" ID="ID_846472947" MODIFIED="1784078534574" TEXT="darf nur weitergehen, nachdem der vorherige Frame aufgegriffen wurde"/>
<node CREATED="1784078681486" ID="ID_1106864388" MODIFIED="1784078714572" TEXT="und darf nur neue IO beauftragen wenn der Zielpuffer vom Consumer freigegeben werden"/>
</node>
@ -2035,102 +2039,82 @@
<node CREATED="1784079589221" ID="ID_1308906754" MODIFIED="1784079609606" TEXT="Optionen">
<node CREATED="1784079611026" ID="ID_870843631" MODIFIED="1784079835110" TEXT="Buffer-Ring k&#xf6;nnte global sichtbar sein">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Sofern er aus dem GUI-Thread alloziert wird, also bevor der Proactor-Thread startet, ist die Position f&#252;r beide Threads sichtbar (und unver&#228;nderlich)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1784079641676" ID="ID_595831960" MODIFIED="1784079776456" TEXT="eine currentFrameNr ebenso">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Diese w&#228;re dann ebenfalls teil der cross-Trade-Synchronisation (ebenfalls bewirkt durch release-Semantik auf dem State-Atomic)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784079653178" ID="ID_1804993764" MODIFIED="1784079727230" TEXT="oder der Displayer hat intern seine eigene FrameNr">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...nur als lokale Variable, die er jeweils hochz&#228;hlt, wenn er einen Trigger bekommt (Nachteil: duplizierter State, dessen Konsistenz sich nur implizit ergibt und nicht offensichtlich ist)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1784079838160" ID="ID_1763616932" MODIFIED="1784080020991" TEXT="k&#xf6;nnte dem Displayer direkt gegeben werden">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Das w&#228;re dann ein Pointer (const void *); seine Sichtbarkeit ergibt sich auch hier aus dem atomic-State
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1784079873496" ID="ID_714454620" MODIFIED="1784079881936" TEXT="kann aber nicht per Dispatcher erfolgen"/>
<node CREATED="1784079882711" ID="ID_102892347" MODIFIED="1784079907595" TEXT="mu&#xdf; dann ein Datenfeld sein (sinnvollerweise im Displayer)"/>
</node>
</node>
<node CREATED="1784080062495" ID="ID_254917759" MODIFIED="1784080174245" TEXT="die zuletzt genanne M&#xf6;glichkeit erscheint mir am nat&#xfc;rlichsten">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...da ich immer in Zust&#228;ndigkeiten denke, ist es mir unsympatisch, einen Satz gemeinsamer Zustands-Variablen zu verwenden
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1784080203172" ID="ID_952531619" MODIFIED="1784080213143" TEXT="daraus ergibt sich die Datenstruktur">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1784080203172" ID="ID_952531619" MODIFIED="1784222609101" TEXT="daraus ergibt sich die Datenstruktur">
<icon BUILTIN="yes"/>
<node CREATED="1784080214847" ID="ID_1165930889" MODIFIED="1784080245515" TEXT="und zwar geh&#xf6;ren damit die Buffer dem Proactor-Thread"/>
<node CREATED="1784080390813" ID="ID_1044489570" MODIFIED="1784080418199" TEXT="Allocation und Ownership">
<node CREATED="1784080461760" ID="ID_1762147278" MODIFIED="1784080535764" TEXT="Buffer sehr gro&#xdf; &#x27f9; einzeln allozieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...das gilt f&#252;r ein Prod-System, in dem der Speicher framentiert ist, und man vermutlich Buffer-Pools hat
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1784080449077" ID="ID_1803805054" MODIFIED="1784080659901" TEXT="einfach w&#xe4;re ein riesiges Array auf dem Heap">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
F&#252;r die Demo ist das die offensichtliche L&#246;sung: man bekommt unique-ownership und Subscript-Operator einfach vom std::unique_ptr. Wenn man den notwendigen Speicher nicht bekommt, dann l&#228;uft die Demo eh nicht, also wozu die Umst&#228;nde?
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="back"/>
</node>
</node>
@ -2165,16 +2149,13 @@
<node CREATED="1784131354971" ID="ID_458757211" MODIFIED="1784131366411" TEXT="diese ist jedoch festgelegt">
<node CREATED="1784131367993" ID="ID_55736035" MODIFIED="1784131407585" TEXT="durch die Forderung nach Lesbarkeit (&#xd83e;&#xdc32; Demo-Code)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
hei&#223;t: hier gibt es keine M&#246;glichkeit, <i>ein Auge zuzudr&#252;cken</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784131409804" ID="ID_359711423" MODIFIED="1784131419742" TEXT="durch das einmal gew&#xe4;hlte Domain-Scoping"/>
</node>
@ -2188,16 +2169,13 @@
</node>
<node CREATED="1784136965047" ID="ID_1744030854" MODIFIED="1784137036831" TEXT="zu kurz gedacht: es ist eine Zugriffs-Klammer">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Eine Operation zum Zugriff und eine Operation um den abgeschlossenen Zugriff zu markieren; zudem gibt es auch noch den Dispatcher.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1784137038242" ID="ID_1199476075" MODIFIED="1784143571509" TEXT="dann doch ein klassisches OO-Callback-Interface">
@ -2215,16 +2193,13 @@
<node CREATED="1784145655032" ID="ID_815825627" MODIFIED="1784145664817" TEXT="macht man das mit C++?"/>
<node CREATED="1784145665437" ID="ID_1276445757" MODIFIED="1784145828191" TEXT="besser direkt mit den POSIX-Funktionen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Mehrere Argumente daf&#252;r: man k&#246;nnte die low-level-Flags setzen (wie <code>O_CLOEXEX, O_NOFOLLOW, </code>oder gar <code>O_DIRECT</code>). Das &#214;ffnen ist <i>atomic,</i>&#160; d.h. das File kann nicht mehr gel&#246;scht/verschoben werden beform man das Filehandle hat (was bei einem getrennten STAT und Path-Check durchaus m&#246;glich ist)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784147238004" ID="ID_973311307" LINK="https://man7.org/linux/man-pages/man2/open.2.html" MODIFIED="1784147251992" TEXT="man: open()"/>
</node>
@ -2236,9 +2211,7 @@
<node CREATED="1784151717872" ID="ID_1035839641" MODIFIED="1784151726466" TEXT="das ist eine spezielle Submission in den URing"/>
<node BACKGROUND_COLOR="#e6dfa8" COLOR="#690f14" CREATED="1784151807158" ID="ID_161068537" MODIFIED="1784152246318" TEXT="&#x27f9; mu&#xdf; URing um +1 Slot gr&#xf6;&#xdf;er dimensionieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Begr&#252;ndung
@ -2261,8 +2234,7 @@
</li>
</ul>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1784151734786" ID="ID_546021072" MODIFIED="1784151801321" TEXT="verwende hier meine Hilfsfunktion f&#xfc;r Chrono &#x27fc; Kernel-Timespec">
@ -2270,8 +2242,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1784143738263" ID="ID_1385880788" MODIFIED="1784152297376" TEXT="Handler-Loop">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1784143738263" ID="ID_1385880788" MODIFIED="1784222595782" TEXT="Handler-Loop">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1784143781367" ID="ID_1463866164" MODIFIED="1784165740325" TEXT="gelesene Daten entgegennehmen">
<icon BUILTIN="button_ok"/>
<node CREATED="1784165743594" ID="ID_465943099" MODIFIED="1784165755001" TEXT="ERRNO steckt im cqe.res">
@ -2300,23 +2272,18 @@
<icon BUILTIN="button_ok"/>
<node CREATED="1784166180437" ID="ID_411616254" MODIFIED="1784166202948">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
verwende hier den zweiten Cursor: <font face="Monospaced" color="#271bd4">nextShow_</font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784166214698" ID="ID_255228689" MODIFIED="1784166237247" TEXT="dar&#xfc;ber auf den State dieses Buffer-Slot zugreifen"/>
<node CREATED="1784166238079" ID="ID_503678940" MODIFIED="1784166404098" TEXT="wenn Daten da sind (state == SHOW) &#x27f9; trigger Displayer">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und zwar via Glib::Dispatcher &#10230; GUI-Thread.
@ -2325,21 +2292,17 @@
Der zugeh&#246;rige Handler-Code l&#228;uft dann bei n&#228;chster Gelegenheit in der GUI-Event-Loop, und holt sich per Callback (Interface PixelFeed) die Daten; in diesem Callback mu&#223; die Read-Barrier realisiert sein, damit der GUI-Thread alle vor diesem Punkt erfolgten Daten&#228;nderungen auch tats&#228;chlich sieht
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1784166414946" ID="ID_1720545051" MODIFIED="1784166498112" TEXT="wenn hier Short-Read markiert &#x27f9; Player anhalten">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Beachte: das darf erst hier passieren, weil wir hier den n&#228;chst niedersten erwarteten Frame anschauen; da wir immer etwas im Voraus lesen, werden wir auch ein end-of-File schon einige Ticks fr&#252;her markieren, m&#252;ssen aber die Schleife noch solange weiterlaufen lassen, bis die letzten erfolgreich gelesenen Frames in die Anzeige &#252;bertragen wurden
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1784166503838" ID="ID_645230473" MODIFIED="1784166532804" TEXT="Bei genauerer Betrachtung : t&#xfc;ckisches Korrektheits-Problem hier">
@ -2350,34 +2313,29 @@
<node CREATED="1784166606710" ID="ID_499843773" MODIFIED="1784166618527" TEXT="in beiden F&#xe4;llen ist der Cursor inkonsitent"/>
<node CREATED="1784166652416" ID="ID_261339357" MODIFIED="1784166866154">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
brauche doch einen 3.Cursor: <font face="Monospaced" color="#5f15de">currShow_</font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#d9b995" COLOR="#690f14" CREATED="1784165814417" ID="ID_312946304" MODIFIED="1784167391017" TEXT="mu&#xdf; zwingend weitere State-Transition einf&#xfc;rhen: WAIT">
<node BACKGROUND_COLOR="#d9b995" COLOR="#690f14" CREATED="1784165814417" ID="ID_312946304" MODIFIED="1784221578895" TEXT="mu&#xdf; zwingend weitere State-Transition einf&#xfc;rhen: FEED">
<arrowlink COLOR="#6574c5" DESTINATION="ID_721661485" ENDARROW="Default" ENDINCLINATION="925;77;" ID="Arrow_ID_86014624" STARTARROW="None" STARTINCLINATION="397;-23;"/>
<arrowlink COLOR="#a03127" DESTINATION="ID_194548529" ENDARROW="Default" ENDINCLINATION="373;0;" ID="Arrow_ID_726120332" STARTARROW="None" STARTINCLINATION="-39;152;"/>
<icon BUILTIN="yes"/>
<node CREATED="1784167398651" ID="ID_977847492" MODIFIED="1784167422438" TEXT="denn der 3.Cursor kann erst hier bewegt werden"/>
<node CREATED="1784167422935" ID="ID_953106086" MODIFIED="1784167433471" TEXT="und es ist nun der 3.Cursor, der vom GUI-Thread verwendet wird"/>
<node CREATED="1784167433959" ID="ID_1914570598" MODIFIED="1784167447134">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
also brauche ich die cross-Thread-Barriere <b>erst hier</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -2409,48 +2367,75 @@
<icon BUILTIN="idea"/>
<node CREATED="1784142980731" ID="ID_1146524396" MODIFIED="1784142995774">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
also <i>keine explizite</i>&#160;Steuerung
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784143310222" ID="ID_1598149960" MODIFIED="1784143340787" TEXT="vielmehr sorgt die feste zyklische Buffer-Struktur f&#xfc;r die Limitierung"/>
<node CREATED="1784143015199" ID="ID_218469149" MODIFIED="1784143039421" TEXT="zwei Phasen schreiten jeweils so schnell voran wie derzeig m&#xf6;glich">
<node CREATED="1784143040300" ID="ID_227838217" MODIFIED="1784143254426" TEXT="Phase-1 : read-Submissions beauftragen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
ist jeweils nur m&#246;glich wenn der daf&#252;r vorgesehene Buffer bereits frei geworden ist
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1784143055979" ID="ID_1727640820" MODIFIED="1784143309405" TEXT="Phase-2 : eingelesene Daten &#xfc;bergeben">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Ist nur m&#246;glich wenn diese Daten f&#252;r den erwartungsgem&#228;&#223; n&#228;chsten Frame bereits da sind, und der unmittelbar vorausgegangene Frame schon komplett vom Video-Displayer &#252;bernommen wurde
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1784221175896" ID="ID_777175706" MODIFIED="1784222589807" TEXT="Ausreifen und Testen">
<icon BUILTIN="pencil"/>
<node CREATED="1784221197676" ID="ID_888318010" MODIFIED="1784221200745" TEXT="Code-Verbesserungen">
<node CREATED="1784221201656" ID="ID_206094356" MODIFIED="1784221217727" TEXT="Getter im BufferDispatcher einf&#xfc;hren">
<node CREATED="1784221218666" ID="ID_847912873" MODIFIED="1784221225933" TEXT="f&#xfc;r current-state und current-frame"/>
<node CREATED="1784221226449" ID="ID_1575870011" MODIFIED="1784221251514" TEXT="verbirgt den kaskadierenden Zugriff &#xfc;ber den outer-Pointer"/>
<node CREATED="1784221253300" ID="ID_1527096674" MODIFIED="1784221274998" TEXT="macht au&#xdf;erdem die Verwendungsstelle lesbarer (Intention im Namen)"/>
</node>
<node CREATED="1784221286953" ID="ID_468705787" MODIFIED="1784221294691" TEXT="State-Flags umbenennen">
<node CREATED="1784221295711" ID="ID_194548529" MODIFIED="1784221395809" TEXT="die Bedeutung von SHOW hat sich ge&#xe4;ndert">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
im State SHOW pasiert jetzt gar nicht mehr das Display, sondern es bedeutet nun, da&#223; der Frame weitergegeben werden mu&#223;
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#a03127" DESTINATION="ID_194548529" ENDARROW="Default" ENDINCLINATION="373;0;" ID="Arrow_ID_726120332" SOURCE="ID_312946304" STARTARROW="None" STARTINCLINATION="-39;152;"/>
</node>
<node CREATED="1784221423989" ID="ID_1367029376" MODIFIED="1784221595584" TEXT="nenne diese Phase jetzt FEED">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
suche einen 4-Buchstaben-Namen, da bin ich &#196;sthet. PUSH w&#228;re auch denkbar, nat&#252;rlich w&#228;re PROPAGATE viel korrekter ;-)
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1784222841942" ID="ID_74519525" MODIFIED="1784222846614" TEXT="zum Laufen bringen">
<icon BUILTIN="flag-pink"/>
</node>
</node>
</node>

View file

@ -40,13 +40,32 @@ auto calcSrcFrame = [](size_t runningNr){ return START_FRAME + runningNr % MAX_L
//-----------------hard-coded-config--
/**
* Read video frames from a raw video file and pass them to OpenGL display.
* Using a »Proactor« thread to coordinate reading via IO_URING.
* Allocates a set of frame-sized buffers (using #RING_SIZ buffers)
* and uses them cyclically, first adding new tasks to read a frame-block
* from the file, receiving completed tasks to hand-over the next frame
* driven by a frame-beat timer, that is also based on IO_URING.
* Each buffer »slot« is associated with a #BuffState flag, implemented
* as `std::atomic` to also serve for cross-thread visibility.
* @note UringVideoPlayer must be constructed in the GTK UI thread,
* yet will immediately launch the »Proactor« thread from the ctor.
* @remarks
* - the source video and layout is hard wired
* - playback will either be looped (if the loop fits into the source video)
* - otherwise, when encountering end-of-file, or when the GTK window is closed,
* the »Proactor« thread will terminate.
* - frames will be submitted to display at the regular timer beats,
* whenever they are ready; frames delayed by IO will slip to the next beat.
*/
class UringVideoPlayer
: NonCopyable
{
using VideoDisplay = EglDisplay<uint8_t, VIDEO_WIDTH,VIDEO_HEIGHT>;
using Buffer = VideoDisplay::Buffer;
enum BuffState { FREE=0, READ, SHOW, WAIT, STOP };
enum BuffState { FREE, READ, FEED, SHOW, STOP };
using AtomicBuffState = std::atomic<BuffState>;
using BufferAlloc = unique_ptr<Buffer[]>;
@ -65,10 +84,15 @@ class UringVideoPlayer
void completedFrame() override;
UringVideoPlayer& player_;
public:
BufferDispatcher (UringVideoPlayer& player)
: player_{player}
{ }
private:
Buffer& getCurrentFrame();
AtomicBuffState& getCurrentFrameState();
};
size_t nextRead_{0};
@ -108,7 +132,7 @@ class UringVideoPlayer
void
UringVideoPlayer::playVideo()
{
/* ===== initial setup ===== */
/* =============== initial-setup =============== */
int inputFD = open (RAW_VIDEO_INPUT_FILE.c_str(), O_RDONLY);
if (inputFD < 0)
__FAIL ("open file \""+RAW_VIDEO_INPUT_FILE+"\"");
@ -122,23 +146,23 @@ UringVideoPlayer::playVideo()
io_uring_queue_init (RING_SIZ+1, &uRing, /*flags*/ 0 ); // Note: one additional »slot« for the timer event(s)
// Setup periodic frame beat, firing infinitely
const auto TICK_TAG = UINT64_MAX; // special marker for timer events (different from possible frame numbers)
const auto TICK_TAG = UINT64_MAX; // special marker for timer events (differs from possible frame numbers)
auto timespec = asKernelTimespec (40ms); // 25fps ≙ 40ms per frame
io_uring_sqe* timerSubmission = io_uring_get_sqe (&uRing);
io_uring_sqe* timerSubmission{io_uring_get_sqe (&uRing)};
io_uring_prep_timeout (timerSubmission, &timespec, /*no count limit*/ 0, IORING_TIMEOUT_MULTISHOT);
io_uring_sqe_set_data64(timerSubmission, TICK_TAG);
addNextReadSubmissions (uRing, inputFD);
io_uring_submit (&uRing);
/* ===== proactor-loop ===== */
/* =============== proactor-loop =============== */
playing_ = true;
while (playing_.load (std::memory_order_acquire))
{
io_uring_cqe* completion;
// blocking wait for the next completion entry
int res = io_uring_wait_cqe (&uRing, &completion);
if (0 > res)
if (res < 0)
__FAIL ("retrieval of io_uring completion failed");
uint64_t tag = completion->user_data;
@ -167,7 +191,7 @@ UringVideoPlayer::addNextReadSubmissions (io_uring& uRing, int inputFD)
assert (0 < inputFD);
const size_t BUFFSIZ = sizeof(Buffer);
while (buffState_[nextRead_ % RING_SIZ] == FREE)
while (FREE == buffState_[nextRead_ % RING_SIZ])
{
size_t srcOffset = calcSrcFrame (nextRead_);
io_uring_sqe* readSubmission = io_uring_get_sqe (&uRing);
@ -191,12 +215,12 @@ UringVideoPlayer::handleCompletedRead (io_uring_cqe& completion)
__FAIL ("async read of frame from video file failed");
size_t frameNr = completion.user_data; // Note: completion was marked with the frameNr as »user data«
auto& state = buffState_[frameNr % RING_SIZ]; // Can thus pick buffer slot based on frame number, modulus ring size
auto& state = buffState_[frameNr % RING_SIZ]; // Can thus pick buffer slot based on frame number, cycling through the ring
if (completion.res != sizeof(Buffer))
state.store(STOP, std::memory_order_relaxed); // Insufficient read, possibly end-of-file => signal stop of playback (to ourselves)
else
state.store(SHOW, std::memory_order_relaxed); // Success; frame data is in the buffer => mark ready for use at the next tick event
state.store(FEED, std::memory_order_relaxed); // Success; frame data is in the buffer => mark ready for use at the next tick event
}
@ -215,12 +239,12 @@ UringVideoPlayer::maybeDisplayFrame()
auto prevState = buffState_[currShow_ % RING_SIZ].load (std::memory_order_relaxed);
auto nextState = buffState_[nextShow_ % RING_SIZ].load (std::memory_order_relaxed);
if (nextState == SHOW and prevState == FREE)
if (nextState == FEED and prevState == FREE)
{ // can hand over this next frame to the VideoDisplay in the GUI-thread
currShow_ = nextShow_;
++nextShow_;
// mark state to wait for display to complete; this also creates a write barrier
buffState_[currShow_ % RING_SIZ].store (WAIT, std::memory_order_release);
// mark state to wait for display completion; this also creates a write barrier
buffState_[currShow_ % RING_SIZ].store (SHOW, std::memory_order_release);
// Notify GUI thread via Glib::Dispatcher
dispatchFrame_();
}
@ -239,9 +263,9 @@ UringVideoPlayer::Buffer*
UringVideoPlayer::BufferDispatcher::retrieveFrame()
{
// retrieve buffer state from proactor-thread; this also creates a read barrier
auto frameState = player_.buffState_[player_.currShow_ % RING_SIZ].load (std::memory_order_acquire);
if (frameState == WAIT)
return & player_.buffer_[player_.currShow_ % RING_SIZ];
auto frameState = getCurrentFrameState().load (std::memory_order_acquire);
if (frameState == SHOW)
return & getCurrentFrame();
else
return nullptr;
// (can happen only in boundary cases)
@ -252,7 +276,22 @@ void
UringVideoPlayer::BufferDispatcher::completedFrame()
{
// mark buffer as display complete for the proactor-thread; this also creates a write barrier
player_.buffState_[player_.currShow_ % RING_SIZ].store (FREE, std::memory_order_release);
getCurrentFrameState().store (FREE, std::memory_order_release);
}
UringVideoPlayer::Buffer&
UringVideoPlayer::BufferDispatcher::getCurrentFrame()
{
auto currFrame = player_.currShow_;
return player_.buffer_[currFrame % RING_SIZ];
}
UringVideoPlayer::AtomicBuffState&
UringVideoPlayer::BufferDispatcher::getCurrentFrameState()
{
auto currFrame = player_.currShow_;
return player_.buffState_[currFrame % RING_SIZ];
}

View file

@ -336,7 +336,7 @@ void
EglDisplay<PIX,WIDTH,HEIGHT>::displayFrame()
{
auto currFrame = pixelFeed_.retrieveFrame();
auto* currFrame = pixelFeed_.retrieveFrame();
if (not currFrame) return;
// pick up current buffer with RGB data