MSVC's verbockter C11-Support
« | 30 Apr 2022 | »Aus meiner Sicht sollte jedes OS und jeder Compiler so funktionieren:
Die wichtigsten OS Features sind durch die C Standard Bibliothek abgebildet. Und die C++ Implementierung baut genau auf diesen C Standard auf.
Microsoft hingegen pfiff bis 2020 auf den C Standard und baute langsamst nur C++11 und C++14 ein.
Doch tief versteckt im Wald, findet man seit 2017 den Header thr/xthreads.h
Eine Geschichte des Verschweigens
Threads,
Mutexe,
Condition-Variablen
und Thread-Local-Storage sind Teil des
C11 Standards.
Der kam damals (2011) auch schon 10 Jahre zu spät, denn eigentlich gibt es
keine Entschuldigung dafür, dass nach dem Jahr 2000 auch nur eine einzige
Sprachvariante auf Threads verzichten musste.
Win32
Threads und POSIX
Threads sind seit Mitte der 90er spezifiziert, und Microsofts fehlende
Condition-Variablen wurden seit den 2000ern mit Workarounds ersetzt.
Es ist also schon lange alles da, was man zu einem übergreifenden Standard
vereinen hätte können.
Doch da Microsoft den C-Teil seines C++ Compilers auf dem Stand von 1989 belassen hatte (nicht mal C99 war unterstützt) und nur auf C++ setzte, hätte diese Story auch hier enden können.
Mit dem MSVC 2013
setzte ein Umdenken ein und plötzlich waren C99 Codes halbwegs fehlerfrei
kompilierbar, was die Kernsprache betraf.
Ich bemerkte das daran, dass libReSSL
plötzlich kompilierbar wurde, welche vom MSVC 2012 abgelehnt worden war.
Endlich konnte ich von OpenSSL
zur libReSSL wechseln.
thrd, mtx und cnd per define
Spätestens 2015 hätte dann meines Erachtens thrd_create
, mtx_lock
und
cnd_signal
auch bei Microsoft ihren Siegeszug antreten können, doch aus
irgend einem seltsamen Grund, wurde das abgewendet und in den Hintergrund
gedrängt.
Denn in MSVC 2015 und MSVC 2017 existiert der Header thr/xthreads.h
inklusive CRT Implementierung, der zwar nicht den C11 Standard umsetzt, aber
fast ausschließlich aus C11-ähnlichen Symbolen besteht.
Tatsächlich fehlen nur noch ein paar Makros, um aus den xthreads
vernünftige C11 Funktionen zu machen.
Und genau das findet auch im GATE Projekt statt, wenn man dieses mit der C11-Option baut:
1#include <thr/xthreads.h> 2 3#define thrd_t _Thrd_t 4#define thrd_start_t _Thrd_start_t 5 6#define thrd_create(thr, fun, arg) _Thrd_create(thr, fun, arg) 7#define thrd_detach(thr) _Thrd_detach(thr) 8#define thrd_exit(code) _Thrd_exit(code) 9#define thrd_join(thr, res) _Thrd_join(thr, res) 10#define thrd_sleep(tm) _Thrd_sleep(tm) 11#define thrd_yield _Thrd_yield 12#define thrd_equal(thr0, thr1) _Thrd_equal(thr0, thr1) 13#define thrd_current _Thrd_current 14 15#define thrd_success _Thrd_success 16#define thrd_nomem _Thrd_nomem 17#define thrd_timeout _Thrd_timedout 18#define thrd_busy _Thrd_busy 19#define thrd_error _Thrd_error 20 21#define mtx_t _Mtx_t 22#define mtx_init(ptr_mtx, mtx_type) _Mtx_init(ptr_mtx, mtx_type) 23#define mtx_destroy(ptr_mtx) _Mtx_destroy(*ptr_mtx) 24#define mtx_lock(ptr_mtx) _Mtx_lock(*ptr_mtx) 25#define mtx_unlock(ptr_mtx) _Mtx_unlock(*ptr_mtx) 26#define mtx_trylock(ptr_mtx) _Mtx_trylock(*ptr_mtx) 27 28#define mtx_plain _Mtx_plain 29#define mtx_timed _Mtx_timed 30#define mtx_recursive _Mtx_recursive 31 32#define cnd_t _Cnd_t 33#define cnd_init(ptr_cnd) _Cnd_init(ptr_cnd) 34#define cnd_destroy(ptr_cnd) _Cnd_destroy(*ptr_cnd) 35#define cnd_signal(ptr_cnd) _Cnd_signal(*ptr_cnd) 36#define cnd_broadcast(ptr_cnd) _Cnd_broadcast(*ptr_cnd) 37#define cnd_wait(ptr_cnd, ptr_mtx) _Cnd_wait(*ptr_cnd, *ptr_mtx) 38#define cnd_timedwait(ptr_cnd, ptr_mtx, ptr_timespec) \ 39 _Cnd_timedwait(*ptr_cnd, *ptr_mtx, (xtime const*)ptr_timespec)
Nachdem diese Ähnlichkeiten also schon so groß sind, frage ich mich, was wohl der Grund war, diese nützliche C-Schicht im Hintergrund zu verstecken.
Absolut unverzeihlich ist aber das, was im MSVC 2019 und 2022 dann gemacht
wurde. Denn hier hat Microsoft den Header thr/xthreads.h
entfernt und
durch den kürzeren Pfad xthreads.h
ersetzt.
Dieser neue xthreads.h
setzt (aus meiner Sicht vollkommen unverständlich)
C++ Vokabel ein und schließt damit die Nutzung von reinem C aus.
Es handelt sich um die exakt gleichen C Funktionen _Thrd_*
, _Mtx_*
,
_Cnd_*
, doch weil irgend ein Vollhonk ein paar typedef
s durch using
ersetzt hat und alles mit dem namespace std
einleitet, wurde die
C Kompatibilität sinnlos zerstört.
Noch abstruser ist, dass im Dev-Blog angekündigt wurde, dass der C11-Library Support in Arbeit ist, doch leider bis zum MSVC 2022 Release noch nicht fertig war.
Wie konnte man das also so verbocken, dass man 2015 quasi schon fertig war (siehe
#define
Beispiel), und 2019 dann alles mit wenigen C++ Phrasen zerstört hat ???
Eine ähnliche Katastrophe ereignete sich auch bei stdatomic.h
, das es
2015 auch schon als xatomic.h
gab. Doch dort lag von Anfang an alles im
C++ Namespace, wobei die Routinen trotzdem die meisten C11
Atomic-Funktionen nachbilden.
Fazit
Im GATE Framework werden letztendlich alle diese Wirrungen der vergangenen
Dekaden sichtbar. Nämlich wenn ich C11 cnd_t
Typen nutze um Semaphoren
nachzubauen und parallel dazu im NT4
Layer mit Win32 Semaphoren
Condition-Variablen simuliert werden.
Hätte es bereits gegen 2003 einen Standard wie C11 gegeben und wäre Microsoft
hierbei fitter gewesen, hätten wir (in einer idealen Welt) bereits 2005 mit
standardisierten C-Threads arbeiten können.
Viele unnötige Workarounds wären uns erspart geblieben und C++ hätte seine
std::threads
auf einem stabilen (und leichtgewichtigen) C-Fundament aufbauen
können.
Ich bezweifle, dass (außer mir) noch andere Entwickler multi-threaded Standard-C Codes in Windows einsetzen. Und das ist mehr als schade, denn die effizientesten Webserver und Sprachcompiler liegen bis heute in reinem C Code vor.
Tja … heute heißt es also:
Früher hätte alles besser werden können.