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, wie viele 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 wurde.
Das ist leider verdammt falsch!
GetFiberData()
ist ein Makro, das
GetCurrentFiber()
und daraus einen Pointer zum Fiber-Datenzeiger ableitet. Dieser wird
dereferenziert und genau 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()
Funktion 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 Schnittstelle.
Meine Lösung ist: IsBadReadPtr(GetCurrentFiber(), sizeof(void*))
Ist dessen Rückgabewert 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 gesichert abfangen.
Fazit
Bug fixed.
Hier sieht man wieder mal, wie dumm das ausgehen kann, wenn man einmal Code schreibt, der auf einem anderen aufbaut, und was passiert, 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 1995 drinnen sein müssen.
However … ich habe vorerst einen Workaround und bin glücklich.