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 OnClientRead und OnClientDisconnect . Im OnClientRead Event Handler lese ich Daten aus einer Socket in einen Puffer, der dem Socket zugeordnet ist (std::map, SocketID als Key, ein Puffer als Value). Im OnClientDisconnect Event Handler lösche ich den Eintrag für den geschlossenen Socket aus der map. Das Problem liegt darin, dass der Event Handler für das OnClientDisconnect Event sofort ausgeführt wird, auch wenn die CPU sich gerade im OnClientRead Event 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 den OnClientRead Event 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 OnClientDisconnect Handler der Socket als tot markiert werden und dieses Flag im OnClientRead Event 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 das OnClientDisconnect Event dazwischenfunkt. Folgendes Szenario könnte ja auch auftreten:

    void OnClientRead()
    {
       if( check_socket_state() )
       {
          receive();
          dispatch();
       }
    }
    

    Wenn OnClientDisconnect zwischen check_socket_state und receive auftritt habe ich das gleiche Problem.

    Zu 2)
    Geht leider nicht, da sich die CPU nicht zwingend im OnClientRead Event 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.


Log in to reply