Event Handler Timing Problem
-
Hallo,
vielleicht hat jemand von euch eine gute Idee, die mir bei der Lösung eines Problems hilft. Ich hab´ das Problem gestern schon im VCL Forum gepostet, kann´s aber hier nochmal kurz zusammenfassen:
Ich habe einen TCP/IP Server, der eventgesteuert arbeitet. Zwei dieser Events sind
OnClientReadundOnClientDisconnect. ImOnClientReadEvent Handler lese ich Daten aus einer Socket in einen Puffer, der dem Socket zugeordnet ist (std::map, SocketID als Key, ein Puffer als Value). ImOnClientDisconnectEvent Handler lösche ich den Eintrag für den geschlossenen Socket aus der map. Das Problem liegt darin, dass der Event Handler für dasOnClientDisconnectEvent sofort ausgeführt wird, auch wenn die CPU sich gerade imOnClientReadEvent Handler befindet. Das führt dazu, dass der Puffer Eintrag für den geschlossenen Socket aus der map gelöscht wird. Anschließend kehrt die CPU in denOnClientReadEvent Handler zurück, wo sie dann über eine dangling reference stolpert. Seltsamerweise findet dabei kein Threadwechsel statt, d.h. der gerade ausgeführte Thread wird nicht durch einen anderen unterbrochen. Damit greifen dann auch Synchronisierungsmaßnahmen wie Critical Sections nicht.Als Lösung fallen mir da nicht wirklich schöne Sachen zu ein, außer weitere Informationen mit dem Puffer zusammen abzulegen und die ggf. zu prüfen. So könnte im
OnClientDisconnectHandler der Socket als tot markiert werden und dieses Flag imOnClientReadEvent Handler berücksichtigt werden. Das Markieren eines Socket müsste dann allerdings atomar sein, damit ich mir nicht andere Timing Probleme einfange.Wenn da jemand einen brauchbaren Ansatz hätte fände ich das super.
PS:
Externe Bibliotheken kommen leider nicht in Frage, leider wird mein Compiler (Codegear RAD Studio 2007) nur bis boost 1.34 unterstützt, sodass ich die boost::asio Bibliothek nicht benutzen kann.Edit:
(1) Typos fixed
(2) Threadüberschrift angepasst
-
Mach dir eine Queue mit Tasks. Wenn dein OnClientDisconnect Handler aufgerufen wurde, entfernst du alle entsprechenden Read-Tasks aus der Queue. Im Read Handler kannst du das prüfen, ob du noch in der Queue bist. Stattdessen kannst du auch einfach OnClientDisconnect im Read behandeln.
-
Zu 1)
Das ist wieder ein Timing Problem, ich kann ja nicht vorhersagen, wann mir dasOnClientDisconnectEvent dazwischenfunkt. Folgendes Szenario könnte ja auch auftreten:void OnClientRead() { if( check_socket_state() ) { receive(); dispatch(); } }Wenn
OnClientDisconnectzwischencheck_socket_stateundreceiveauftritt habe ich das gleiche Problem.Zu 2)
Geht leider nicht, da sich die CPU nicht zwingend imOnClientReadEvent Handler befindet. Wenn die Gegenstelle den Socket normal schließt, ohne jemals ein Telegramm verschickt zu haben bekäme der Server nichts davon mit.
-
Was, wenn du im Disconnect einen neuen Thread erstellst, und dann durch z.B. Mutexes absicherst?
-
Das könnte funktionieren, werd´s mal ausprobieren.