Forum: Mikrocontroller und Digitale Elektronik Liste thread-unsaver Funktionen?


von Rolf F. (Gast)


Lesenswert?

Wo gibt es zumindest für (ANSI-)C eine Liste der thread-unsaven
Funktionen?

Von strtok ist ja bekannt dass es thread-unsave ist, aber welche
anderen Funktionen sind es noch?

von Peter D. (peda)


Lesenswert?

Was bedeutet "thread-unsaven" ?

Hat das nur was mit C++ zu tun ?

In normalen ANSI-C Büchern finde ich es jedenfalls nicht.


Peter

von Frank Linde (Gast)


Lesenswert?

Hallo Peter,

das ist der neudeutsche Ausdruck für etwas, was uC-Programmierer
ohnehin verinnerlicht haben (sollten): Wenn eine Routine eine andere
Routine unterbricht und nach getaner Arbeit wieder zur Ursprungsroutine
zurückkehrt, dann gibt es zwei Möglichkeiten: Es wurden alle Register
und ggf. Variablen zuvor gesichert und am Ende wiederhergestellt, dann
ist das "thread-safe", oder man hat das nicht getan, dann ist die
Routine "thread-unsave". Das ist jedenfalls ein simples Beispiel. Das
Thema kann man wahlweise auch beliebig komplex gestalten... ;-)

Gruß, Frank

von Peter D. (peda)


Lesenswert?

Wozu muß man das wissen ?

Das ist doch nicht mein Problem, da hat sich gefälligst der Compiler
drum zu kümmern.

Wenn ich eine Variable später verwende, muß entweder der Aufrufer sie
sichern oder der Aufgerufene. Je nach Maschine kann das eine oder das
andere günstiger sein.

Das ist also keine C-Frage, sondern eine Frage der Implementierung des
Compilers. Selbst in verschiedenen Versionen eines Compilers kann das
völlig unterschiedlich gelöst sein.


Wissen müßte man sowas also nur, wenn man Assembler includiert, aber
sowas mache ich nicht. Bzw. wenn doch, dann lasse ich mir vom Compiler
einen Rumpf basteln (nach Assembler compilieren) und füge dort meinen
Assemblercode ein.


Peter

von Frank Linde (Gast)


Lesenswert?

>> da hat sich gefälligst der Compiler
drum zu kümmern.

Yepp, sollte er, macht er vermutlich auch. AFAIK ist das auch kein
Problem des Compilers, sondern der Bibliotheken. Ich kenne das Thema
auch eher aus dem Bereich der PC-Programmierung, denn da gibt es jede
Menge Bibliotheken, die eben nicht thread-safe sind. Von C habe ich
nicht genug Ahnung, um auf die Ursprungsfrage antworten zu können.

Gruß, Frank

von Rolf F. (Gast)


Lesenswert?

Also tread-unsave bedeutet, dass die funktion nicht von mehreren Threads
(ohne getrennten Speicherbereich) benutzt werden kann; das hat mit
Registern usw. nix zu tun.
strtok ist thread-unsave, weil es nur nur einen buffer verwendet und
alle Threads darauf zugreifen. Bei den meisten anderen Funktionen wie
z. B. strlen gibt es das Problem nicht.

Und bisher habe ich noch keinen Compiler gesehen, der bei tread-unsaven
Funktionen warnt, selbst wenn der Code mit solchen Funktionen immer
einen Seiteneffekt produziert.

Im Embedded-Bereich bekommt man vom Compiler meist nicht einmal eine
Warnung bei Anweisungen ohne Effekt (z. B. {a==b;}) oder auch nur
undefinierten Ausdrücken (z. B. {a[i] = i++;} oder memmove(b, ++b,
9)).

Deshalb suche ich noch eine Liste der tread-unsaven Funktionen.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Das kann von Compiler zu Compiler unterschiedlich sein; bei ICC-430 sind
alle Funktionen bis auf strtok reentrant, bei anderen Compilern weiß
ich es nicht. Eine Frage beim Hersteller/in der entsprechenden
Mailingliste sollte Klarheit schaffen.

Andreas

von Rolf F. (Gast)


Lesenswert?

Aha.
Also ist es wohl so, dass nach dem ANSI-Standard nur strtok nicht
thread-save ist, oder?

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Der C-Standard sagt über Threads überhaupt nichts aus, ob die
Standard-Bibliothek thread-safe ist oder nicht ist also vollkommen
abhängig von der Implementation.

von Rolf F. (Gast)


Lesenswert?

Also bei strtok steht es im Standard und auch in Büchern wie
"Programmieren in C"; ansonsten wäre der Standard ja überflüssig!

Was dort nicht festgelegt ist, ist welche Operationen atomar sind, aber
das hängt ja auch von der Hardware ab.

von Frank Linde (Gast)


Lesenswert?

>>  das hat mit Registern usw. nix zu tun.

Und ob! Das Register-Beispiel ist nur die unterste Ebene des Problems.
Nach dem Compilieren geht es auf Maschinenebene sehr wohl um Register.
Denn wenn die betreffende Funktion sich schon nicht um die Register
kümmert, dann helfen besondere Maßnahmen auf höheren Ebenen (Buffer
sichern, etc.) auch nichts mehr.

Wenn es um eine möglichst schnelle Ausführung einer Funktion geht, kann
man z.B. das Statusregister temporär in ein anderes freies Register
laden und später daraus zurückkopieren. Das bringt beim AVR z.B. eine
Einsparung von 2 Taktzyklen gegenüber der Sicherung auf dem Stack. Das
funktioniert beim "normalen" Aufruf dieser Funktion auch prima.

Wird diese Funktion jetzt aber während ihrer Ausführung von einem
Interrupt unterbrochen und die Interruptroutine ruft die Funktion
erneut auf, dann knallt es. Diese Lösung ist also nicht thread-safe,
während die Stack-Lösung durchaus wiedereintrittsfähig sein kann, wenn
innerhalb der Routine nicht noch anderer Murks passiert.

Gruß, Frank

von Rolf F. (Gast)


Lesenswert?

> Wenn es um eine möglichst schnelle Ausführung einer Funktion geht,
> kann man z.B. das Statusregister temporär in ein anderes freies
> Register laden und später daraus zurückkopieren. ... Wird diese
> Funktion jetzt aber während ihrer Ausführung von einem
> Interrupt unterbrochen und die Interruptroutine ruft die Funktion
> erneut auf, dann knallt es.

Ja, aber das kann nur in Assembler passieren, denn nach dem Standard
sind die meisten Funktionen thread-save und können problemlos mehrfach
aufgerufen werden. Da darf es höchstens Seiteneffekte bei nicht
atomaren Operationen geben. Also Funktionen mit Umlagern von Registern
sind garantiert nicht ANSI-C-kompatibel!

von Frank Linde (Gast)


Lesenswert?

Mag sein. Da Peter aber auch viel Assembler macht, fand ich die
Erläuterung auf Assemblerebene angebracht, da sie das Prinzip
verdeutlicht. Aus C-Diskussionen halte ich mich mangels tiefergehender
Kenntnisse lieber raus, aber wenn Du Dir so sicher bist, daß die
notwendigen Angaben im Standard festgelegt sind, warum schaust Du denn
nicht dort nach?

Gruß, Frank

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

@Rolf F.:
An welcher Stelle im Standard steht das? Ich lese auf Seite 184 von
http://wwwold.dkuug.dk/jtc1/sc22/open/n2794/n2794.txt:

[#4]   The   functions  in  the  standard  library  are  not
       guaranteed to be  reentrant  and  may  modify  objects  with
       static storage duration.146)

Und die dazugehörige Fußnote:

       146Thus,  a signal handler cannot, in general, call standard
          library functions.

von Peter D. (peda)


Lesenswert?

Wenn ich das richtig verstanden habe, dann ist mit "thread-save"
einfach nur reentrant gemeint.

Da wir hier ja über Mikrokontroller reden, dürfte das nur in wenigen
Fällen überhaupt eine Rolle spielen.

Beim Keil C51 sind generell alle Funktionen nicht reentrant. Das ist
auch sinnvoll, damit man auch die kleinen mit nur 128 Byte RAM effektiv
in C programmieren kann.

Und sobald man dieselbe Funktion im main() und in einem Interrupt
aufruft, gibts ein Warning.
Dann muß man diese Funktion als reentrant definieren, was dann aber
mehr Speicherverbrauch und längere Laufzeit bewirkt.
Oftmals fährt man dann günstiger, wenn man diese Funktion nochmal mit
einem anderen Namen definiert (zumindest laufzeitmäßig).
Ob Bibliotheksfunktionen auch als reentrante Version vorliegen, habe
ich noch nicht probiert.

Generell sollen ja Interrupts sehr schnell sein, d.h.
Unterprogrammaufrufe und erst recht langdauernde Stringoperationen
sollte man da nur sehr sparsam einsetzen.


Aber ich stimme auch den meisten zu, daß solche Feinheiten nur an der
jeweiligen Version eines Compilers und seiner Bibliotheken festzumachen
sind und nicht generell für C gelten.


Peter

von Rolf F. (Gast)


Lesenswert?

Aha, dann sind also die Funktionen von stdlib.h generell nicht
thread-save und die anderen aber schon (sofern nichts anderes daneben
steht wie bei strtok).
Ich hatte das nicht gefunden, weil ich den Standard nicht komplett
gelesen und nach thread und nicht reentrant gesucht hatte.

Dann muss ich mal in der vorhandenen Software nachsehen, ob in den ISRs
etwas aus stdlib.h ist und das thread-save ersetzen.

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

"standard library" bedeutet nicht stdlib.h, sondern alle
Standard-Funktionen aus string.h, strdlib.h, math.h, usw.

Obwohl der C-Standard nicht garantiert dass die Funktionen reentrant
sind, können sie es je nach Implementation natürlich sein.

von Rolf F. (Gast)


Lesenswert?

Achso. In dem Standard konnte ich keine Definition von "standard
library" finden (der Begriff wird zwar verwendet, aber nicht
definiert) und bin deshalb davon ausgegangen, dass damit stdlib.h
gemeint war.
Wenn das nicht so ist, ist damit wohl der Umfang der "Standard
headers", also
            <assert.h>    <inttypes.h>  <signal.h>    <stdlib.h>
            <complex.h>   <iso646.h>    <stdarg.h>    <string.h>
            <ctype.h>     <limits.h>    <stdbool.h>   <tgmath.h>
            <errno.h>     <locale.h>    <stddef.h>    <time.h>
            <fenv.h>      <math.h>      <stdint.h>    <wchar.h>
            <float.h>     <setjmp.h>    <stdio.h>     <wctype.h>
gemeint, oder?
Weil im Standard nicht steht, dass die anderen Bibliotheken nicht
reentrant sein könnten, folgt dass sie reentrant sein müssen, oder?

von Frank Linde (Gast)


Lesenswert?

Hhm, die Erfahrung zeigt, dass man solche Texte besser anders herum
interpretiert: Wenn die Wiedereintrittsfähigkeit nicht ausdrücklich
zugesichert ist, dann sollte man sich auch nicht darauf verlassen.

Gruß, Frank

von Peter D. (peda)


Lesenswert?

Ich hab mir mal die BC-Hilfe zu strtok angesehen, jetzt kapier ichs
endlich.

"thread-unsaven" hat überhaupt nichts mit reentrant zu tun.

strtok kann man nämlich auch mit einen Null-Zeiger aufrufen, um in dem
selben String das Token ein weiteres mal zu suchen, d.h. die Funktion
merkt sich, wo sie das letzte mal gestanden hat.

Also auch, wenn eine Funktion ein Token sucht, und dann eine
Unterfunktion aufruft, die zwischenzeitlich ein anderes Token sucht,
dann krachts.

Da aber ein Zeiger auf den gefundenen Teilstring geliefert wird,
braucht man sich diesen ja nur zu merken um danach die nächste Suche zu
beginnen und alles ist in Butter.


"thread-unsaven" sind also alle Funktionen, die einen weiteren
Durchgang machen können, aber dafür kein extra Datenfeld vom Aufrufer
belegen, wo sie irgendwas ablegen können.

Z.B. ist findfirst/findnext "thread-saven", da der Aufrufer eine
entsprechende Datenstruktur zur Verfügung stellen muß. Sonst könnte man
ja nicht rekursiv einen Verzeichnisbaum durchsuchen.


Peter

von Joerg Wunsch (Gast)


Lesenswert?

> "thread-unsaven" sind also alle Funktionen, die einen weiteren
> Durchgang machen können, aber dafür kein extra Datenfeld vom
> Aufrufer belegen, wo sie irgendwas ablegen können.

Diese sind es auf jeden Fall.

Generell sind alle Funktionen nicht thread-safe, die sich in
irgendeiner Form Zwischenergebnisse ,,merken'' (also die in ihrer
Implementierung irgendwo Variablen benutzen, die als ,static'
deklariert sind).  Das sind mindestens die von Dir genannten, können
aber noch mehr sein (wenn die Implementierung sich auf diese Weise
irgendwas vereinfachen wollte).

Die Unixe haben für alle Funktionen, die vom API her nicht thread-safe
sind (das sind die von Dir genannten) mittlerweile alternative
Funktionen implementiert, die den Kontext als Parameter übergeben
bekommen, so daß der Aufrufer für dessen Zwischenspeicherung zuständig
ist.  Die Konvention dabei ist, an den normalen Funktionsnamen ein _r
anzuhängen.  Für das Beispiel strtok() ist das dann also strtok_r().

avr-libc folgt dieser Konvention übrigens, wobei außer strtok_r()
derzeit nur noch random_r() und rand_r() implementiert sind.

von Frank Linde (Gast)


Lesenswert?

>> "thread-unsaven" hat überhaupt nichts mit reentrant zu tun.

Das sehe ich immer noch anders. Wenn eine Routine "reentrant" ist,
dann ist sie automatisch auch "tread-safe".

Gruß, Frank

von Rolf F. (Gast)


Lesenswert?

> Da aber ein Zeiger auf den gefundenen Teilstring geliefert wird,
> braucht man sich diesen ja nur zu merken um danach die nächste Suche
> zu beginnen und alles ist in Butter.

Also der Normale Aufruf von strtok sieht doch so aus, dass der zweite
u. die folgenden Aufrufe mit NULL und nicht mit dem Teilstring
durchgeführt wird und damit funktioniert es nicht thread-save.
Es sollte thread-save sein, wenn es immer mit dem Teilstring (ungleich
NULL) aufgerufen wird.
Reentrant ist im Wesentlichen allgemeiner als thread-save, denn
reentrant bedeutet, dass die Funktion auch in einem Speicherbereich
mehrfach laufen kann, z. B. auf 4 CPUs des Rechnerns gleichzeitig.
Thread-save heißt ja nur, dass verschiedene Threads die Funktion auch
in einem Speicherbereich ohne Seiteneffekt benutzen können; es sagt
nichts darüber aus, ob die Funktion auch reentrant ist.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.