Thread oder Fiber

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.