Ersatz für WaitableTimer in Windows CE 6.0



  • Hallo zusammen.

    Unter einem "normalen" Windows Programm nutze ich in einem Thread statt des weitverbreiteten "Sleep(..)" einen Waitable Timer, um die Thread Pausendauer zu steuern.
    Die Pausendauer ist sehr lange (5 bis 10 Minuten).

    Der Thread kann aber auch von Aussen durch setzen von Flags gesteuert werden,
    um eine bestimmte Zusatzfunktion auszuführen.
    Damit es hierbei keine Zeitverzögerung gibt, wird der WaitableTimer ebenfalls von Aussen durch "SetWaitableTimer()" zur vorzeitigen Ausführung gebracht.
    Das ist ja auch wichtig, wenn ich den Thread beenden will.

    Nun wollte ich diese Funktionalität in mein WinCE-Programm übertragen.
    Aber leider gibt es dort diesen "WaitableTimer" nicht.
    Und mit "Sleep(..)" geht das nicht.

    Meine Fktn., stark verkürzt:

    void WtSleep(HANDLE &hTimer, LONGLONG nanosecs)
    {
        liDueTime.QuadPart = nanosecs;
        hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
        ...
        SetWaitableTimer((hTimer), &liDueTime, 0, NULL, NULL, 0);
        ...
        WaitForSingleObject((hTimer), dwMaxWait);
        ...
        CancelWaitableTimer(hTimer);
    }
    

    Kann mir da bitte jemand weiterhelfen?

    Grüsse
    Hemut


  • |  Mod

    Nimm SetTimer und eine Timer Funktion.



  • Hi Martin,
    bin dabei, mich da einzulesen.
    Ein kleines Beispiel wäre nicht schlecht.
    Eines, das genau wie Sleep(..) einsetzbar ist.
    Habe da im Netz leider noch nichts entsprechendes gefunden.
    Also wenn jemand sowas hätte?



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

    Ein kleines Beispiel wäre nicht schlecht.

    Dir ist aber schon klar, daß auf der MSDN-Seite zu SetTimer() auch ein Link zu einem ausführlichem Beispiel ist???



  • Dieses Beispiel hatte ich an vielen Stellen gesehen.
    Alle fast derselbe Code.
    .....
    Ich brauche diese Message Loop in einem Fenster, um den Event abzufangen.
    Aber gerade das wollte ich nicht!
    Ich habe in meinem Thread auch gar kein Fenster.
    Vielleicht bin ich auch zu blöd, um das für meine Zwecke zu adaptieren.

    Diese Message Loop wäre ja dann zentraler Bestandteil meiner Thread Loop.
    .. Das verstehe ich noch.
    Wo bekomme ich aber den Fenster-Handle her?

    Aber geht das nicht auch mit dem "WaitForSingleObject(..)" ???
    Vielen Dank an Euch alle.


  • |  Mod

    Nein. Der Diskussionen sind vor 15 Jahren schon genügend geführt worden.

    Wo ist Dein Problem? Du benötigst kein Fenster mit eine TimerProc!
    Und wenn Du den Timer an ein Fenster binden willst ist das auch nicht schlecht und CreateWindow gibt es auch auf WIndows CE. 😉

    Ab und Zu PeekMessage/GetMessage aufzurufen... sollte auch nicht das Problem sein.



  • @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.



  • Also entschuldigt bitte.
    Leider war ich bei den Diskussionen vor 15 Jahren nicht dabei.
    Und leider finde ich diese wohl auch nicht im Netz.
    Ich habe inzwischen mindestens 1 Tag danach gesucht.
    Und meine Versuche, das zu implementieren, sind leider nicht von Erfolg gekrönt.



  • @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.
    Sie ist auch im Einsatz, und ich konnte dabei noch keine Probleme feststellen.



  • Was mache ich also falsch?

    Wenn ich doch kein Window-Handle und keine Callback Fktn brauche, dann müsste ich doch mit folgendem Code zurechtkommen?
    Dieses "TranslatMessage" und "DispatchMessage" dürfte ich dann auch nicht brauchen?
    Und wenns so einfach ist, müssten die paar Zeilen doch funktionieren?

    Leider gibt mir schon die "SetTimer(..)" einen seltsamen Wert zurück.

    #define  IDT_TIMER  WM_USER + 200
    void TimerSleep(DWORD msecs)
    {
    	MSG			msg;
    	UINT_PTR		uResult;
    
    	uResult = SetTimer(NULL, IDT_TIMER, msecs, NULL);
    	if (uResult == 0) 
    	{ 
    		return; 
    	}
    	while (GetMessage(&msg, NULL, 0, 0))
            { 
                if (msg.message == IDT_TIMER) 
                { 
                    break; 
                }
            } 
            KillTimer(NULL, IDT_TIMER);
    }


  • @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.