Ersatz für WaitableTimer in Windows CE 6.0



  • @elmut19 sagte in Ersatz für WaitableTimer in Windows CE 6.0:

    @hustbaer sagte in Ersatz für WaitableTimer in Windows CE 6.0:

    @elmut19
    Ich würde bei dem was du beschreibst an einen EVENT denken. Ich hätte da von Anfang an keinen waitable timer verwendet.

    Davon abgesehen: die WtSleep Funktion sieht auf den ersten Blick aus als ob sie zwei Bugs hätte: ein Race und ein Leak.

    Hi hustbaer,
    diese function ist bei Microsoft genauso zu finden.
    Ich habe sie hier ja auf die Angabe der zentralen Anweisungen beschränkt.

    Ich hab ja auch "sieht auf den ersten Blick aus als ob" geschrieben. Weil man eben den restlichen Code nicht sieht.
    Was aber auffällig ist:

    • Da drinnen wird ein Timer erzeugt aber nicht freigegeben. Die Vermutung liegt nahe dass der Timer evtl. nie freigegeben wird. Das wäre dann ein Leak.
    • Da drinnen wird eine Variable, die per Referenz reingegeben wird, auf das Timer-Handle gesetzt. Die Vermutung liegt nahe dass dann während die Funktion wartet ein anderer Thread das Timer Handle aus der Variable ausliest und verwendet um SetWaitableTimer aufzurufen. Das wäre dann ein Race.

    Sie ist auch im Einsatz, und ich konnte dabei noch keine Probleme feststellen.

    Das heisst gar nix. Wenn die Funktion selten genug aufgerufen wird (wobei zigtausendfach bei einer Ausführung vermutlich schon "selten genug" sein kann), dann wirst du das Leak nicht merken. Und das Race wirst du vermutlich sowieso nicht merken. Was nicht heisst dass es nicht trotzdem da ist und nicht trotzdem ein Bug ist.

    Also vorausgesetzt der restliche Code sieht so aus wie ich annehme dass er aussieht. Kann ich ja nur raten.

    Aber ganz davon abgesehen: Verwende doch einfach EVENTs wie ich geschrieben habe: https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventw



  • @hustbaer
    Ja, ich übergebe den Timer-Handle in der Original-Version, und ich behandle ihn auch ausserhalb der Fktn.
    Für den neuen Fall brauche ich ja was Anderes, da es keinen Waitable Timer gibt.

    Und zu den Events:
    diesen muss ich ja über einen Timer auslösen. Und wo habe ich dann den Timer?

    Aber hier mal mein ursprünglicher Code, aus der anderen Anwendung: (nun aber nicht zu gebrauchen, weil WaitableTimer nicht existent!)

    void WtSleep(HANDLE &hTimer, LONGLONG nanosecs)
    {
        //HANDLE hTimer = NULL;
        LARGE_INTEGER liDueTime;
    
    
        while (!TryEnterCriticalSection(&g_csCriticalSectionWaitableTimer))
            Sleep(STD_WAITTIME);
    
        liDueTime.QuadPart = nanosecs;//-100.000.000LL; = 10 sec. ( in 100 nanosecond intervals )
    
        if(!hTimer){
            // Create an unnamed waitable timer.
            hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
            if (NULL == hTimer){
    		    TRACE("CreateWaitableTimer failed (%d)\n", GetLastError());
                LeaveCriticalSection(&g_csCriticalSectionWaitableTimer);
                return;
            }
        }
        // Set a timer  ( in 100 nanosecond intervals )
        if (!SetWaitableTimer((hTimer), &liDueTime, 0, NULL, NULL, 0)){
    		TRACE("SetWaitableTimer failed (%d)\n", GetLastError());
            LeaveCriticalSection(&g_csCriticalSectionWaitableTimer);
            //CloseHandle(hTimer);
            //hTimer = NULL;
            return;
        }
        LeaveCriticalSection(&g_csCriticalSectionWaitableTimer);
    
        // Wait for the timer. ( "nanosecs" ist hier niemals so gross, als dass es nicht in einen "int" passen würde.)
        DWORD dwWait;
        DWORD dwMaxWait;
        LONGLONG llMaxWait = nanosecs / 5000LL; // HP: Die Division durch 5000 ergibt auch die Umrechnung in Millisekunden.
        LONGLONG llWait = nanosecs / 10000LL; // HP: Die Division durch 5000 ergibt auch die Umrechnung in Millisekunden.
        dwWait = abs((int)llWait); // HP: Gehe auch hier davon aus, dass "INT_MIN" nicht unterschritten wird.
        if((llMaxWait < INT_MAX) && (llMaxWait > INT_MIN)){// HP: Wenn "== INT_MIN", wird "INT_MIN" zurückgegeben.
            dwMaxWait = abs((int)(llMaxWait)); // HP: Maximal die doppelte Zeit des gewünschten Intervalls warten.
        }else
            dwMaxWait = 60000; // HP: Dürfte nur im Fehlerfall passieren. Damit abgebrochen wird, wird 1 Minute gesetzt.
        //if (WaitForSingleObject((hTimer), INFINITE) != WAIT_OBJECT_0){// HP: Wäre im Fehlerfall eine Blockade.
        if (WaitForSingleObject((hTimer), dwMaxWait) != WAIT_OBJECT_0){// HP: "dwMaxWait" ist wieder in Millisekunden!
    		TRACE("WaitForSingleObject failed (%d)\n", GetLastError());// HP: debug
        }
        //TRACE("DoWaitableTimerSleep: (%d), Wait=%d ms, MaxWait=%d ms, elapsed Time:%d\n",hTimer,dwWait,dwMaxWait,GetTickCount());// HP: debug
    
        CancelWaitableTimer(hTimer);
        //CloseHandle(hTimer);   // wird ausserhalb gemacht
        //hTimer = NULL;
    }
    

  • |  Mod

    1. SetTimer kann man so nicht benutzen, Wie kommst Du auf so was aus der Doku?
    2. Der Code würde doch blockieren. Was ist der Unterschied zu einem gnadenlosen Sleep?

    Wie ich das verstehe willst Du doch gerade extern auf etwas warten.

    Wenn Du einen Waitable Timer nachbauen willst mach folgendes:

    1. Erzeuge einen eigenen Thread
    2. In dem Thread erzeugst Du einfach ein Fenster (natürlich nicht sichtbar, oder verwendest einen Callback
    3. Du baust eine Funktion, die einen Timer registriert...
      3a. Die Funktion erzeugt ein Handle merkt es sich und gibt das zurück
      3a. Die Funktion trägt den Timer in eine Liste ein (gestaffelt nach Wartezeit oder so) und triggert den Thread, dass sich was geändert hat.
    4. Der Thread nimmt den nächsten Timer aus der Liste, der fällig werden müsste und wartet.
      Entweder mit einer Callback Funkion oder einem Fenster.
    5. Das Event des erreichten Timers wird gesetzt
    6. Der Thread nimmt den nächsten Timer aus der Liste...

    Mal ganz grob gesehen dürfte das ja nicht schwer sein...



  • @elmut19 sagte in Ersatz für WaitableTimer in Windows CE 6.0:

    Ja, ich übergebe den Timer-Handle in der Original-Version, und ich behandle ihn auch ausserhalb der Fktn.

    Ja, und genau da hast du dein Race. Wenn du statt HANDLE ein atomic<HANDLE> verwenden würdest, dann wäre es OK. (Vorausgesetzt du würdest atomic<HANDLE> richtig verwenden natürlich.) EDIT: Ne, das ist ja die CRITICAL_SECTION die verwendet wird um die Zugriffe zu synchronisieren, das passt dann schon. /EDIT

    Für den neuen Fall brauche ich ja was Anderes, da es keinen Waitable Timer gibt.
    Und zu den Events:
    diesen muss ich ja über einen Timer auslösen. Und wo habe ich dann den Timer?

    Du verwendest ja sowieso nur relative Timeouts. Also nimm für den "Timer" Teil einfach den Timeout Parameter von WaitForSingleObject und das "im Falle des Falles vorzeitig Wecken" machst du indem du den Event mit SetEvent setzt.



  • @Martin-Richter Wenn ich ihn richtig verstehe will er bloss ein von Aussen unterbrechbares Sleep. Daher auch mein Vorschlag einen Event zu verwenden.



  • Danke Martin,

    ich wollte exakt einen Sleep(..) nachbauen.
    Nur dass ich diesen von extern auch noch auslösen kann.

    Anhand der Dokus, die ich gefnden habe, wird mir dieser Ablauf, den Du schilderst bei Weitem nicht klar.
    Ich weiss auch nicht, wo das hätte stehen sollen.
    Überall besteht ein Timer nur aus dem "#define", wie in dem, was ich hier eingestellt habe.
    Und mit den optionalen Parametern komme ich genau auf meine Lösung.
    Und die funktioniert anscheinend nicht.

    Aber ich werd mal in der Richtung weiter suchen.



  • @hustbaer
    Und danke auch Dir.
    Auf die Idee mit dem Timeout von WaitFor... bin ich noch nicht gekommen.
    Hört sich gut an. Und wäre wohl doch die einfachste Lösung.
    Das probier ich mal!

    Das mit dem "atomic" werd ich mir auch durch den Kopf gehen lassen.
    Aber bisher funktioniert das so, wie es ist.
    Ohne die CriticalSections hatte ich allerdings Probleme, da ich das in zwei Threads verwende.
    Die Microsoft-Version hat je den Handle lokal. Da gibts die Probleme ja nicht.
    Man kann sie daher aber nicht von Aussen setzen.


  • |  Mod

    Wenn Du nur einen Timer brauchst.

    1. Erzeuge ein Event auf das Du als Timer wartest
    2. Starte einen Thread und übergib ihm Handle und Wartezeit
    3. Nun kannst Du im ersten Thread das Handle benutzen.
    4. Der Thread mach einfach nur einen Sleep für die angegebene Zeit, setzt das Handle und terminiert.

    Das geht solange gut, wie Du eben nicht alle Nase lang einen neuen Thread für jeden Timer erzeugen musst und die Wartezeit lange ist. Ansonsten musst Du eine Verwaltung bauen, wie ich es vorgeschlagen habe.

    Ansonsten hat CE ja auch andere nette Sachen wie MsgWaitForMultipleObjects...



  • @elmut19 sagte in Ersatz für WaitableTimer in Windows CE 6.0:

    Ohne die CriticalSections hatte ich allerdings Probleme, da ich das in zwei Threads verwende.

    Self-🤦♂
    Sorry. Die Critical-Sections hab ich geistig ausgeblendet. Natürlich sollten die reichen das Race zu verhindern. Aber die waren halt auch in deinem ursprünglich gezeigten Code nicht drinnen 🙂



  • Vielen Dank nochmal an Euch alle.

    Ich habe nun die Version von hustbaer implementiert.
    Sie funktioniert.

    Es ist schon seltsam.
    Man hat eigentlich die Lösung schon vor sich, sieht sie aber nicht.
    Also das mit dem WaitFor.. und einfach dessen Timeout-Parameter als eigentlichen Timer benutzen.