Skip to content

Architektur Threading

Michael Massee edited this page Jul 27, 2026 · 1 revision

Architektur: Threading-Regeln (VCL/UNO-UI)

Diese Seite richtet sich an Entwickler, die am Plugin-Code arbeiten. Für die Bedienung des fertigen Plugins siehe die Turniersysteme-Seiten.

Die eiserne Regel

Jede VCL-/UNO-UI-Operation muss auf dem LibreOffice-Main-Thread laufen. Dazu gehören u. a.:

  • window.dispose(), Fenster-/Control-Neuaufbau
  • XFixedText.setText(), XPropertySet.setPropertyValue(...) auf UI-Models/Peers
  • XLayoutManager.requestElement() / showElement()
  • setVisible(...)

Warum das kritisch ist

VCL ist durch die SolarMutex geschützt und nicht thread-safe. Die UNO-Bridge synchronisiert nur Inter-Prozess-Aufrufe, nicht in-process-Java-Calls — ein direkter UI-Aufruf aus einem Fremd-Thread geht ungebremst an VCL vorbei an der Absicherung.

Folgen eines Verstoßes:

  • Deadlock/Freeze – besonders unter Windows reproduzierbar; das Linux-Backend ist toleranter und maskiert den Bug oft, was das Problem beim Testen auf Linux unsichtbar machen kann
  • SIGSEGV – harter Absturz

Ein typisches Symptom einer solchen Race ist paradox: „mit TRACE-Logging langsamer, aber stabiler" — das zusätzliche Logging verändert das Timing und maskiert das Rennen.

Wo Hintergrund-Threads ins Spiel kommen

Mehrere Infrastruktur-Komponenten feuern Callbacks nicht auf dem Main-Thread:

Quelle Threading
SheetRunner.benachrichtigeListener() eigener SheetRunner-Worker-Thread
TimerManager.emittiere() PTM-Timer-Executor-Thread (bei jedem Tick)
WebServerManager / ReleaseUpdateService-StatusListener eigene Threads

Jeder Listener/Callback, der aus einem dieser Threads feuert, darf niemals direkt UI anfassen. Stattdessen: Zustand im Fremd-Thread lesen, dann die UI-Arbeit per LoMainThread.post(xContext, () -> ...) auf den Main-Thread marshallen.

LoMainThread.post reiht das Runnable via AsyncCallback/PostUserEvent in die Main-Thread-Queue ein (FIFO, läuft erst nach dem aktuellen Event) — das löst zugleich den Thread-Wechsel und die nötige VCL-Re-Entranz aus.

Referenz-Implementierungen im Code: ProcessBox (runOnMain), InfoSidebarContent (aufMainThread), SheetListeSidebarContent, TimerToolbarSteuerung.

Ausnahme: reines Pull-Statusmelden

Wenn ein Listener nur einen FeatureStateEvent feuert und LibreOffice sich den Zustand selbst thread-sicher abholt (z. B. ProtocolHandler.notifyAllListeners()), ist kein Marshalling nötig — hier greift die UNO-Bridge-Synchronisierung normal.

Methodenreferenz statt Inline-Lambda

Bei neuem Marshalling-Code eine Methodenreferenz bevorzugen:

LoMainThread.post(xContext, this::aktualisiereAnzeige);   // bevorzugt
LoMainThread.post(xContext, () -> aktualisiereAnzeige());  // vermeiden

Der Grund ist werkzeugbedingt: Das projektinterne ArchUnit-Gate (siehe unten), das Off-Thread→VCL- Zugriffe über Klassengrenzen hinweg erkennt, faltet inline-Lambdas in die umschließende Methode ein — der Marshalling-Schnitt wird dadurch im Aufruf-Graphen unsichtbar und korrekt marshallte Pfade erscheinen fälschlich als Verstoß. Eine Methodenreferenz wirkt dagegen als echter Schnitt im Graphen.

Automatisierte Absicherung im Projekt

Das Projekt sichert diese Regel zweistufig automatisiert ab:

  1. Quelltext-Scan (HintergrundListenerVclKonventionTest): schlägt fehl, wenn eine Klasse einen Fremd-Thread-Listener registriert/implementiert und eindeutige VCL-Control-APIs referenziert, ohne LoMainThread/runOnMain zu nutzen.
  2. Call-Graph-Analyse (ThreadingCallGraphArchTest, ArchUnit): verfolgt den klassenübergreifenden Aufruf-Graphen ab den bekannten Off-Thread-Wurzeln (TimerListener, ITurnierEventListener, IGlobalEventListener, ProtocolHandler.notifyAllListeners) und meldet jede erreichbare UNO-UI/VCL-Senke.

Wer neuen Listener-Code schreibt, sollte vor dem Commit prüfen: Feuert dieser Callback auf einem Fremd-Thread? Fasst er UI an? Wenn beides zutrifft, ist LoMainThread.post Pflicht.

Clone this wiki locally