Aufgabe 6 piper


Kritische Abschnitte sollen immer so kurz wie möglich sein :slight_smile:


Großzügig = sequenzielles Programm? :wink:


Nein ;). Ich meinte, ob wir uns Gedanken darum machen sollen, ob man vllt. durch Umbauen des Algorithmus, den wir verwenden, Anweisungen im kritischen Abschnitt sparen können.


Was für Ideen hättest du, die kritischen Abschnitte zu minimieren? Bzw. welche Abschnitte kommen dir zu lang vor?

Eigentlich sind die schon ziemlich kurz. Gibt auch keine Probleme wie “Ausgabe im kritischen Abschnitt”, weil das Locking eh woanders stattfindet.


So wie ich die sbuf.c implementiert habe, komme ich momentan zu einer folgenden Struktur:

Initialisierungen;
Lock();
 if(...) {
   Unlock();
   return;
 }
SBUF verändern;
Unlock();
return;

Und ich kann es wohl umbauen zu:

Initialisierungen;
Lock();
 if(...) {
   Unlock();
   return;
 }
Unlock();
Mit Zeigern auf SBUF lesend zugreifen;
Lock();
SBUF verändern;
Unlock();
return;

Wobei ich mir bei zweiter Variante Gedanken machen muss, ob ich verschiedene Locks brauche und ob es überhaupt so geht.
Und die Alternative ist es einfach die ganze Methode zu synchronisieren und wenn das keine Punktabzüge gibt wäre es die weniger fehleranfällige Variante ;).


Allgemein musst du bei dem [m]unlock[/m]/[m]lock[/m] extrem aufpassen, dass es da keine Race-Conditions gibt. Häufig kann sich dazwischen dann doch irgendein Zustand ändern, sodass es kaputt geht.

Ich denke nicht, dass man das bei der piper sinnvoll trennen kann, da reicht ein [m]lock[/m] am Anfang und ein [m]unlock[/m] am Ende der Funktion. Auch kostet jedes Lock Zeit, ggf. mehr als man spart, wenn man einfach von Anfang bis Ende gelockt hat.

Falls du aber ne gute Idee hast, nur zu.


Woran liegt es, dass valgrind Speicherlecks entdeckt? An der fehlenden remove()-Funktion des sbuf?
Ist eine Implementierung der Aufgabe ohne Speicherleck überhaupt möglich? Immerhin kommt auch die Referenzimplementierung nicht ungeschoren durch valgrind…


Richtig. Es gibt keine Funktion, die den [m]SBUF[/m] wieder zerstört. Deswegen kommt man um Speicherlecks nicht herum.

Die API sollte da in Zukunft verbessert werden.

Außerdem sind beim [m]exit(3)[/m] wohl noch die [m]FILE *[/m] zu den Pipes offen, diese nutzen auch [m]malloc(3)[/m]. Je nachdem wie man die Threads erstellt hat, benötigen diese auch noch Speicher. Diese Lecks könnte man (und sollte man) aber auch mit der aktuellen API vermeiden.


Mit dem Wissen aus SP1 ist eine Implementierung der [m]piper[/m] ohne Speicherleck nicht möglich.


Also soll man alle offenen Pipes am ende schließen?
Und soll man auch die Semaphoren, die in der SBUF hängen zerstöhren?


Kann man, wenn man will. Ist aber für eine korrekte Abgabe nicht nötig (und auch nicht ganz so leicht).


Die [m]sbufGetNumberOfSems()[/m] würde kaputt gehen, wenn man [m]NULL[/m] übergibt. In dem h-Datei steht aber nichts von return-Werten im Fehlerfall. Soll man das dann einfach nicht behandeln? Bis jetzt hab ich nur ne spezielle Fehlermeldung, aber es wird danach trotzdem die fehlschlagende Funktion ausgeführt.


Abstuerzen darf es in keinem Fall.

Fehlermeldungen in Bibliotheksfunktionen sind eher ein Kandidat fuer Punktabzug :stuck_out_tongue:


Die Schnittstelle gibt leider keinen speziellen Wert für den Fehlerfall vor. Daher würde ich einfach 0 zurückgeben, wenn ein NULL-Zeiger übergeben wird.


Irgendwo hab ich das tatsächlich schonmal gehört… :wink: danke für den Hinweis! Gilt das nur für eigene Fehlerausgaben oder soll man auch bei einem malloc fail keine perror Ausgabe machen?

Dann habe ich noch eine Frage, in der manpage vom fwrite steht nichts darüber drin, ob im Fehlerfall die errno gesetzt wird. Jetzt habe ich ein bisschen gegoogelt und was gefunden, dass alle stdio funktionen die errno im Fehlerfall setzen müssen - zumindest nach dem posix standard. Bin jetzt verwirrt…


In Bibliotheksfunktionen darf es niemals eine Fehlerausgabe geben (es sei denn das Programm wird terminiert). Natürlich auch dann keine, wenn [m]malloc(3)[/m] fehlschlägt. [m]perror(3)[/m] kann der Aufrufer aufrufen, falls er den Fehlergrund herausfinden will.

Stell dir einfach vor „was würde malloc machen“? [m]malloc(3)[/m] erzeugt im Fehlerfall auch keine Ausgabe.

Ja, [m]fwrite(3)[/m] und co. setzen die [m]errno[/m]. Das liegt einfach daran, dass sie [m]write(2)[/m] verwenden - und das setzt die [m]errno[/m]. Ist nicht ganz so schön, dass das nicht in der [m]fwrite(3)[/m] steht.


Cool, genau das macht die Referenzimplementierung :wink:


Ich finde nen Crash da jetzt nicht schlimm. Wenn man die Funktion falsch verwendet (in der API steht nichts davon, was bei [m]NULL[/m] passiert), dann kann es auch abstürzen. Aber kann man auch anders implementieren.


Nach der darfst du dich sowieso nicht richten, ich sprech da aus Erfahrung :wink:


das wirkt so, als sollte man sie dann nicht Referenzimplementierung nennen :smiley: