
Thread oder Fiber
« | 18 Dec 2022 | »Der Aufruf von Sleep() schickt einen Thread schlafen. Aber wenn der Thread ein Fiber ist, wäre es sinnvoller, andere Fibers zu aktivieren, damit die was sinnvolles tun können.
Doch wie weiß man jetzt, ob man in einem Thread oder in einem Fiber läuft?
Vorgeschichte: Crash auf ARM64
Ich schicke Threads selten schlafen, am ehesten warte ich auf Bedingungen mit Events, Conditions oder anderen Objekten. Eine Ausnahme gibt es allerdings: Bei der Ermittlung der aktuellen CPU Auslastung. Denn hierfür soll der aktuelle Thread wirklich “schlafen gehen”, um danach festzustellen, wieviel CPU Aktionen in der Zwischenzeit außerhalb passiert sind.
Tja und heute sehe ich zufällig einen Crash in meiner Sleep-Implementierung.
In dieser wollte ich nämlich genau diese Unterscheidung machen:
- Ist es ein Thread, rufe die OS
Sleep()
Routine auf. - Ist es ein Fiber, führe ein
yield()
aus, damit was anderes abgearbeitet werden kann.
Und für die Unterscheidung nutzte ich einfach GetFiberData()
und erwartete
einen NULL
-Pointer, wenn kein Fiber gestartet worden war.
Das ist leider verdammt falsch!
GetFiberData()
ist ein Makro, das GetCurrentFiber()
und daraus einen
Pointer zum Fiber-Datenpointer ableitet. Dieser wird dereferenziert und
hier dreht einem der Kernel den Hals um.
GetCurrentFiber()
liefert nämlich immer einen Nicht-NULL
-Pointer, der
aber außerhalb eines Fibers auf “irgendwas” zeigt, nur nicht auf gültige
Daten.
Lösung
Windows Vista führte
die IsThreadAFiber()
ein, die diese Frage eindeutig klärt. Das heißt,
vor einem Get*Fiber*()
Aufruf, prüft man, ob es sich überhaupt um einen
Fiber handelt.
Doch was ist mit Windows XP und älter? Denn hier fehlt diese API.
Meine Lösung ist: IsBadReadPtr(GetCurrentFiber(), sizeof(void*))
Ist das nämlich TRUE
laufen wir in einem Thread, ist es FALSE
,
ist ein Fiber am Werk.
Man könnte natürlich auch per __try/__except/__finally
die Dereferenzierung
des Pointers probieren.
Fazit
Bug fixed.
Hier sieht man wieder mal, wie dumm das ausgehen kann, wenn man einmal Code schreibt, der auf einem anderen aufbaut, und wenn man dann 1 Jahr später Koroutinen und Fibers einwebt. Schon ändert sich das Verhalten für einen Fall, den man nicht abgetestet hat.
Mich verwundert, dass IsThreadAFiber()
erst 2006 mit Vista eingeführt wurde.
Ein solches If
-Tool hätte schon im originalen NT 4 drinnen sein müssen.
However … ich habe vorerst einen Workaround und bin glücklich.