Buffer-Lebensdauer bei Pipeclient mit FILE_FLAG_OVERLAPPED?



  • Steht doch ganz klar in der MSDN

    Bei WriteFile

    lpBuffer

    This buffer must remain valid for the duration of the write operation. The caller must not use this buffer until the write operation is completed.



  • Moment mal, @.........:
    Da ist garnichts klar.
    Was ist eine "write operation"? Ein WriteFile() Aufruf bis zur Rückkehr, oder ein abgeschlossener Sendevorgang, ...

    Aber nun vermute ich es schon. Es ist wie eine Datei. Sobald WriteFile() zurückkehrt, darf der buffer ungültig sein.
    Dann ist es also einfacher, als ich dachte.

    Danke auch für die Links.
    MfG



  • Sobald WriteFile() zurückkehrt, darf der buffer ungültig sein.

    Wenn du Overlapped I/O verwendest und GetLastError() nach dem WriteFile() ERROR_IO_PENDING zurückgibt darf der Puffer nicht verändert oder gelöscht werden.



  • Wie würdet ihr dann ein mehr oder weniger zeitkritisches Loggingsystem bauen?
    Da, wo WriteFile() aufgerufen wird, muss es sofort zurückkehren. Es werden aber manchmal tausende Messages über die Pipe gehen.
    Müsste ich dann eine Warteschleife erstellen, mit den einzelnen Overlapped-Strukturen, einem manuellen Event usw...?
    Geht das nicht einfacher?


  • Mod

    Ich würde in dem Proztess eine Queue anlegen, die die Nachrichten speichert und einen zweiten Thread der die Nachrichten über die Pipe rausschaufelt.
    Zu viele Threads machen alle nur langsam.

    Aber wie zeitkritisch ist das Ganze denn? Ein bischen in eine Pipe zu schreiben ist extrem schnell.



  • Naja, klar ist das schreiben schnell. Aber nicht synchron, oder? Wenn da der Server mal hängen sollte, hängt der Client doch auch (Wäre ein Timeout unter sagen wir 500ms überhaupt sinnvoll? Eine Message hat so 1KB also, und es sind maximal ca. 5000 pro Sekunde).
    Ist synchron eigentlich generell langsamer als asynchron? Oder ist da kaum/kein Unterschied, besonders wenn das ganze lokal stattfindet?



  • Wobei wenn, wie in meinem Fall, die zu sendenden Daten sehr flüchtig sind, könnte ich doch gleich eine Kopie-Queue + eine SYNC pipe verwenden... Wozu dann noch mit Wartereien rumspielen...



  • Martin Richter schrieb:

    Ich würde in dem Proztess eine Queue anlegen, die die Nachrichten speichert und einen zweiten Thread der die Nachrichten über die Pipe rausschaufelt.

    Genau. Wollte ich grad schreiben 😞
    😉

    MrY schrieb:

    Naja, klar ist das schreiben schnell. Aber nicht synchron, oder?

    Was verstehst du unter synchron? Natürlich muss der Sender nicht warten bis der Empfänger die Daten abgeholt hat...

    Wenn da der Server mal hängen sollte, hängt der Client doch auch (Wäre ein Timeout unter sagen wir 500ms überhaupt sinnvoll? Eine Message hat so 1KB also, und es sind maximal ca. 5000 pro Sekunde).

    "Server hängt -> Client hängt" ist ein übliches Szenario, ja. Irgendwann sind die Sende-Puffer beim Sender und die Empfangs-Puffer beim Empfänger voll, und dann fängt die Sender-Seite an zu blockieren.

    Und ... 5000 Messages pro Sekunde sind schon nicht ganz wenig. Da würdest du vermutlich auch schon von der Worker-Thread Lösung profitieren, da der Worker-Thread dann, wenn die Rechenzeit eng wird, auch mehrere Messages auf einmal in die Pipe jagen kann. Das verringert den Overhead pro Message deutlich.

    Ist synchron eigentlich generell langsamer als asynchron? Oder ist da kaum/kein Unterschied, besonders wenn das ganze lokal stattfindet?

    Das kommt auf den Treiber/zuständigen Systemteil an, der die Operation implementiert. Bei Pipes kann ich nichts dazu sagen. Auf jeden Fall *können* synchrone Operationen schneller sein als asynchrone, nämlich dann, wenn der Treiber/zuständige Systemteil sie sofort abschliessen kann. Wieviel das um ist habe ich aber noch nicht ausprobiert.
    Ob es Fälle gibt wo asynchrone Operationen schneller sein können als synchrone weiss ich nicht. Vermutlich auch 😉



  • Also Moment, Zusammenfassung:

    Es sollen Daten von einem Clientprozess zu einem Serverprozess gesendet werden.
    Der Clientprozess bekommt die Daten in einem DirectPlay-Callback. Die Daten sind allerdings nur innerhalb von eben diesem gültig. Das Senden muss allerdings sofortig, ohne jegliches Warten, erfolgen.

    1. Option:
    Client sendet im DirectPlay-Callback einfach synchron (Beidseitig im blocking mode). Dabei wird ein 100ms Timeout festgelegt. Somit sollte der Callback höchstens wenig bis garnicht aufgehalten werden.
    Trotzdem gefällt mir das nicht so Recht. Vielleicht wegen des Timeouts.

    2. Option:
    Client kopiert im Callback die Daten in eine Queue.
    Ein extra Thread prüft ständig auf neue Einträge und sendet diese gegebenfalls (synchron, extra Thread ist ja nicht zeitkritisch).
    Hier gefällt mir nicht, dass der extra Thread unnötig Resourcen verbrauchen würde (Endlosschleife ala if(!queue.empty())...).

    3. Option
    Client sendet im DirectPlay-Callback asynchron (Overlapped). Gleichzeitig wird die Overlapped-Struktur und die Daten zusammen gespeichert (Gesendet wird mit einem Zeiger auf diese Daten).
    Ein extra Thread wartet in einer Endlosschleife mit WaitForMultipleObjects(). Wird eine Operation abgeschlossen, werden die Daten anhand des gespeicherten Overlapped/Daten-Paars gelöscht.
    Allerdings steht auf http://www.ddj.com/windows/184416624 in der Mitte bei "Pipe Fault Handling in Code" etwas davon, dass man ein overlapped WriteFile() nicht machen soll, bevor nicht das vorige "completed" wurde...
    Und auch, dass es (dadurch?) passieren kann, dass eine operation niemals fertiggestellt wird.
    Man kann aber ein timeout für eine overlapped operation setzen? Oder ist gemeint, dass nach einer gewissen Zeit der Overlapped/Daten-Eintrag einfach verworfen wird? So würde ich es dann wohl machen...

    Option 1 naja... Option 2 resourcenfressend... Option 3 optimal?
    Was meint ihr?

    Danke euch jedenfalls!



  • Hier gefällt mir nicht, dass der extra Thread unnötig Resourcen verbrauchen würde (Endlosschleife ala if(!queue.empty())...).

    Nein, würde er nicht, weil man natürlich eine Condition-Variable (bzw. einen Event) verwenden würde, um ihn aus seinem wohlverdienten Schlummer zu wecken, sobald es etwas zu tun gibt.

    Also WaitForSingleObject() statt Polling auf !queue.empty().

    Option 3: blödsinn. Kleines Detail am Rande: wenn man den Thread der die IO Operation startet nicht kontrolliert, kann man asynchrones IO vergessen. Wenn der Thread nämlich beendet werden sollte, dann gehen die IOs die er abgesetzt hat mit hopps.

    Ne, ehrlich, Option 2 ist optimal. Natürlich nur wenn man sie nicht dumm implementiert *g*



  • Das mit dem Event ist mir im Bett auch grad eingefallen.
    Deinen Einwand verstehe ich nicht ganz. Die noch nicht angekommenen Messages (Also pending IOs) gehen verloren, wenn der Thread abgemurkst wird?

    Allerdings mache ich es jetzt natürlich mit einem Event.
    Jedes queue.push_back() signalisiert es, der Worker-Thread wartet darauf, leert die Queue, resettet das Event und weiter gehts wieder von vorn...



  • wenn der Thread abgemurkst wird

    Threads soll man nicht abmurksen, sondern geordnet herunterfahren.
    Simon



  • Ich meinte @hustbaers "beenden".
    Außerdem kann man Threads nicht immer geordnet herunterfahren! Sei es ein Blocking-Call oder ein Prozess-Crash...



  • MrY schrieb:

    Deinen Einwand verstehe ich nicht ganz. Die noch nicht angekommenen Messages (Also pending IOs) gehen verloren, wenn der Thread abgemurkst wird?

    Die gehen auch verloren, wenn der Thread sich "ganz normal" brav selbst beendet, indem z.B. die Thread-Funktion "return;" macht.

    So. Hat einige Zeit gedauert bis ich die Stelle in der MSDN wiedergefunden habe, wo das beschrieben steht. Aber hier ist sie (Doku zu ExitThread): http://msdn.microsoft.com/en-us/library/ms682659(VS.85).aspx

    MSDN schrieb:

    When this function is called (either explicitly or by returning from a thread procedure), the current thread's stack is deallocated, all pending I/O initiated by the thread is canceled, and the thread terminates. (...)

    BTW: Wenn man mit asynchronen IOs arbeitet, den Thread der diese startet selbst kontrolliert, und vermeiden möchte dass man zu früh die Thread-Funktion verlässt, kann man dazu GetThreadIOPendingFlag verwenden. Ein einfacher Polling Loop (mit nem Sleep drinnen) reicht hier wohl meistens.

    Außerdem kann man Threads nicht immer geordnet herunterfahren! Sei es ein Blocking-Call oder ein Prozess-Crash...

    Dann muss man es eben bleiben lassen 🙂 TerminateThread ist auf jeden Fall keine Lösung. Wenn man warten kann, dann warten, sonst halt TerminateProcess.

    Nochwas: ab Vista oder Windows 7 (weiss ich nimmer, und bin grad zu faul noch weiter zu suchen) werden nichtmehr alle IOs gecancelt, sondern nurmehr die, die nicht einem IO Completion Port zugeordnet sind. Da hat wohl jemand mitgedacht (spät, aber besser als nie).


Anmelden zum Antworten