Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. CancelWaitableTimer bug when used with socket?

CancelWaitableTimer bug when used with socket?

Scheduled Pinned Locked Moved C / C++ / MFC
helpcomquestion
7 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    ehaerim
    wrote on last edited by
    #1

    In normal situations, the following code stops timer as expected: CreateWaitableTimer... SetWaitabltTimer... // with duration 0 and period USER_TIMER_MINIMUM CancelWaitableTimer... Here, I mean 'normal' as 'not using socket calls'. However, when the two conditions are met, CancelWaitableTimer does not stop timer. I've tried to find out why, in vain. The two conditions are: - CSocket::Connect or CAsyncSocket::Connect was called - the last SetWaitableTimer was called with Duration 0 and period USER_TIMER_MINUMUM Actually USER_TIMER_MINUMUM is only to repeat the issue everytime in 100%. If period is long enough, this issue happens irregularly. That is, the following code fails to stop the timer. CreateWaitableTimer... SetWaitabltTimer... // with duration 0 and period USER_TIMER_MINIMUM CSocket::Connect... // use Connect from CSocket or CAsyncSocket CancelWaitableTimer... Since it is too hard to post a complete source code here, I would email the zipped project file if you send me an email to 'ehaerim at gmail dot com'. thx HaeRim Lee

    O 1 Reply Last reply
    0
    • E ehaerim

      In normal situations, the following code stops timer as expected: CreateWaitableTimer... SetWaitabltTimer... // with duration 0 and period USER_TIMER_MINIMUM CancelWaitableTimer... Here, I mean 'normal' as 'not using socket calls'. However, when the two conditions are met, CancelWaitableTimer does not stop timer. I've tried to find out why, in vain. The two conditions are: - CSocket::Connect or CAsyncSocket::Connect was called - the last SetWaitableTimer was called with Duration 0 and period USER_TIMER_MINUMUM Actually USER_TIMER_MINUMUM is only to repeat the issue everytime in 100%. If period is long enough, this issue happens irregularly. That is, the following code fails to stop the timer. CreateWaitableTimer... SetWaitabltTimer... // with duration 0 and period USER_TIMER_MINIMUM CSocket::Connect... // use Connect from CSocket or CAsyncSocket CancelWaitableTimer... Since it is too hard to post a complete source code here, I would email the zipped project file if you send me an email to 'ehaerim at gmail dot com'. thx HaeRim Lee

      O Offline
      O Offline
      Orjan Westin
      wrote on last edited by
      #2

      First of all, what do you mean when you say "fails to stop the timer"? Does CancelWaitableTimer return an error, or does the execution stop at CSocket::Connect? Details, please. (Also, it helps if you use the formatting options when posting, to separate text from code.) Have you tried using Windows sockets directly? The MFC socket classes are very poorly implemented (see here[] for more details), and it's generally not that much more work to write your own. (Hm. Maybe I ought to dig out the implementation I wrote that I own and write it up as an article.)

      E 1 Reply Last reply
      0
      • O Orjan Westin

        First of all, what do you mean when you say "fails to stop the timer"? Does CancelWaitableTimer return an error, or does the execution stop at CSocket::Connect? Details, please. (Also, it helps if you use the formatting options when posting, to separate text from code.) Have you tried using Windows sockets directly? The MFC socket classes are very poorly implemented (see here[] for more details), and it's generally not that much more work to write your own. (Hm. Maybe I ought to dig out the implementation I wrote that I own and write it up as an article.)

        E Offline
        E Offline
        ehaerim
        wrote on last edited by
        #3

        > Orjan Westin wrote: > First of all, what do you mean when you say "fails to stop the timer"? > Does CancelWaitableTimer return an error, or does the execution stop at CSocket::Connect? > Details, please. CancelWaitableTimer(CWT) returns TRUE, but fails to stop the timer activated by SetWaitableTimer(SWT). You can download the complete source code from ftp://ftp1.investware.net:1250/Temp/WT3.zip Compiling and running the sample should NEVER raise an ASSERT if CWT works correctly, that is, CWT returning TRUE MUST stop the timer activated by the last SWT. WTDlg.cpp lines 14-17 show some #define statements. [1] First simple test case in which no socket is used and CWT returning TRUE works as expected and succeeds in stoping the timer

        #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
        #define WAITABLETIMER_DUETIME_INITIALLY (0)

        //#define TEST_WITH_SOCKET

        [2] Second abnormal test case in which socket is used and CWT returns TRUE, but fails to stop the timer, and therefore ASSERT is triggered.

        #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
        #define WAITABLETIMER_DUETIME_INITIALLY (0)

        #define TEST_WITH_SOCKET

        [3] You can test a variety of cases by modifying the first two #define statements. The most significant factor is the value of 'WAITABLETIMER_PERIDO'. When it is - USER_TIMER_MINIMUM * 1, ASSERT is triggered 100%. - ~ USER_TIMER_MINIMUM * 50, ASSERT is triggered 100% as far as I tested. - USER_TIMER_MINIMUM * 75, ASSERT is triggered 1~2 times out of 5 executions. - USER_TIMER_MINIMUM * 100, ASSERT is triggered 1~2 times out of 10 executions. The less significant but still affecting the result is the value of 'WAITABLETIMER_DUETIME_INITIALLY'. When it is -1, much lower possibility of ASSERT. Now, I believe you have all that you can play around. In conclusion, all I can say is that CWT returning TRUE does not stop the timer immediately, but after the timer is signaled one more time for the following sequences: - SetWaitableTimer is called with Duration 0 and period USER_TIMER_MINIMUM - CSocket::Connect or CAsyncSocket::Connect is called - CancelWaitableTimer is called If you find out why CWT returning TRUE fails to stop the timer, please let me know. thx

        L 1 Reply Last reply
        0
        • E ehaerim

          > Orjan Westin wrote: > First of all, what do you mean when you say "fails to stop the timer"? > Does CancelWaitableTimer return an error, or does the execution stop at CSocket::Connect? > Details, please. CancelWaitableTimer(CWT) returns TRUE, but fails to stop the timer activated by SetWaitableTimer(SWT). You can download the complete source code from ftp://ftp1.investware.net:1250/Temp/WT3.zip Compiling and running the sample should NEVER raise an ASSERT if CWT works correctly, that is, CWT returning TRUE MUST stop the timer activated by the last SWT. WTDlg.cpp lines 14-17 show some #define statements. [1] First simple test case in which no socket is used and CWT returning TRUE works as expected and succeeds in stoping the timer

          #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
          #define WAITABLETIMER_DUETIME_INITIALLY (0)

          //#define TEST_WITH_SOCKET

          [2] Second abnormal test case in which socket is used and CWT returns TRUE, but fails to stop the timer, and therefore ASSERT is triggered.

          #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
          #define WAITABLETIMER_DUETIME_INITIALLY (0)

          #define TEST_WITH_SOCKET

          [3] You can test a variety of cases by modifying the first two #define statements. The most significant factor is the value of 'WAITABLETIMER_PERIDO'. When it is - USER_TIMER_MINIMUM * 1, ASSERT is triggered 100%. - ~ USER_TIMER_MINIMUM * 50, ASSERT is triggered 100% as far as I tested. - USER_TIMER_MINIMUM * 75, ASSERT is triggered 1~2 times out of 5 executions. - USER_TIMER_MINIMUM * 100, ASSERT is triggered 1~2 times out of 10 executions. The less significant but still affecting the result is the value of 'WAITABLETIMER_DUETIME_INITIALLY'. When it is -1, much lower possibility of ASSERT. Now, I believe you have all that you can play around. In conclusion, all I can say is that CWT returning TRUE does not stop the timer immediately, but after the timer is signaled one more time for the following sequences: - SetWaitableTimer is called with Duration 0 and period USER_TIMER_MINIMUM - CSocket::Connect or CAsyncSocket::Connect is called - CancelWaitableTimer is called If you find out why CWT returning TRUE fails to stop the timer, please let me know. thx

          L Offline
          L Offline
          Lost User
          wrote on last edited by
          #4

          Hi, I looked over your code. The behavior is exactly as documented: CancelWaitableTimer function[^]

          The CancelWaitableTimer function does not change the signaled state of the timer. It stops the timer before it can be set to the signaled state and cancels outstanding APCs. Therefore, threads performing a wait operation on the timer remain waiting until they time out or the timer is reactivated and its state is set to signaled. If the timer is already in the signaled state, it remains in that state.

          Your due time is set to the minimum 0xA value (one NT quantum). By the time you have called CancelWaitableTimer[^]... the timer is already in the signaled state. If you want to reset a timer that is already in the signaled state... call the SetWaitableTimer function[^] to manually reset the timer to the non-signaled state and then call the CancelWaitableTimer function[^]. In other words... the code within your

          #if defined(TEMP_WORKAROUND)

          is correct way to cancel the timer. Best Wishes, -David Delaune

          E 1 Reply Last reply
          0
          • L Lost User

            Hi, I looked over your code. The behavior is exactly as documented: CancelWaitableTimer function[^]

            The CancelWaitableTimer function does not change the signaled state of the timer. It stops the timer before it can be set to the signaled state and cancels outstanding APCs. Therefore, threads performing a wait operation on the timer remain waiting until they time out or the timer is reactivated and its state is set to signaled. If the timer is already in the signaled state, it remains in that state.

            Your due time is set to the minimum 0xA value (one NT quantum). By the time you have called CancelWaitableTimer[^]... the timer is already in the signaled state. If you want to reset a timer that is already in the signaled state... call the SetWaitableTimer function[^] to manually reset the timer to the non-signaled state and then call the CancelWaitableTimer function[^]. In other words... the code within your

            #if defined(TEMP_WORKAROUND)

            is correct way to cancel the timer. Best Wishes, -David Delaune

            E Offline
            E Offline
            ehaerim
            wrote on last edited by
            #5

            David, First of all thx for your kind response. But, I think you have read the code a bit wrong way withing the limit of my knowledge.

            Randor wrote:

            Your due time is set to the minimum 0xA value (one NT quantum).

            This is wrong because I set the initial due time and period as follows at line 15-16

            #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
            #define WAITABLETIMER_DUETIME_INITIALLY (0)

            It is the period that set to 0xA, not the initial due time. The initial due time and period is intentionally set to 0 and 0xA to fire the timer 'immediately first time and ,after then, in the period of every 0xA milliseconds'. Because of 'immediate due time', the timer gets signaled 'immediately' literally, and therefore 'case WAIT_OBJECT_0 + 1:' statement is the first statement executed without any delay. Then socket Connect fails with WSAEWOULDBLOCK error and finally CWT call returns TRUE in sequence. So, the overall code flow is like this:

            pWTDlg->m_hTimerRC = CreateWaitableTimer(NULL, FALSE, NULL); // automatic reset timer
            bRet = SetWaitableTimer(pWTDlg->m_hTimerRC, 0, 0xA, NULL, NULL, NULL); // immediate, periodic(10ms) timer // TRUE
            pWTDlg->m_bCreated = sock.Create(); // TRUE
            pWTDlg->m_bConnected = sock.Connect(...); // FALSE with WSAEWOULDBLOCK
            bRet = CancelWaitableTimer(pWTDlg->m_hTimerRC); // TRUE

            Since the last CancelWaitableTimer returns TRUE, it should stop the timer right away, but the timer gets signaled just one more time. That's the issue! Questions here are - Why is the timer not stopped even after CWT returns TRUE? - Why is the timer always signaled just one more time, not two or three and so on even if I admit the timer can be signaled more times after CWT returns TRUE(of course, I don't admit this, but just for the discussion of this issue, though)? - What's the use of CWT if it is not guaranteed to stop the timer immediately? I'd rather set INFINITE due time and/or INFINITE period for SWT instead of using CWT. I hope I miss something but unfortunately I don't know what are they. Please be very specific and detail explaining what's going on about this issue. regards

            L 1 Reply Last reply
            0
            • E ehaerim

              David, First of all thx for your kind response. But, I think you have read the code a bit wrong way withing the limit of my knowledge.

              Randor wrote:

              Your due time is set to the minimum 0xA value (one NT quantum).

              This is wrong because I set the initial due time and period as follows at line 15-16

              #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 1)
              #define WAITABLETIMER_DUETIME_INITIALLY (0)

              It is the period that set to 0xA, not the initial due time. The initial due time and period is intentionally set to 0 and 0xA to fire the timer 'immediately first time and ,after then, in the period of every 0xA milliseconds'. Because of 'immediate due time', the timer gets signaled 'immediately' literally, and therefore 'case WAIT_OBJECT_0 + 1:' statement is the first statement executed without any delay. Then socket Connect fails with WSAEWOULDBLOCK error and finally CWT call returns TRUE in sequence. So, the overall code flow is like this:

              pWTDlg->m_hTimerRC = CreateWaitableTimer(NULL, FALSE, NULL); // automatic reset timer
              bRet = SetWaitableTimer(pWTDlg->m_hTimerRC, 0, 0xA, NULL, NULL, NULL); // immediate, periodic(10ms) timer // TRUE
              pWTDlg->m_bCreated = sock.Create(); // TRUE
              pWTDlg->m_bConnected = sock.Connect(...); // FALSE with WSAEWOULDBLOCK
              bRet = CancelWaitableTimer(pWTDlg->m_hTimerRC); // TRUE

              Since the last CancelWaitableTimer returns TRUE, it should stop the timer right away, but the timer gets signaled just one more time. That's the issue! Questions here are - Why is the timer not stopped even after CWT returns TRUE? - Why is the timer always signaled just one more time, not two or three and so on even if I admit the timer can be signaled more times after CWT returns TRUE(of course, I don't admit this, but just for the discussion of this issue, though)? - What's the use of CWT if it is not guaranteed to stop the timer immediately? I'd rather set INFINITE due time and/or INFINITE period for SWT instead of using CWT. I hope I miss something but unfortunately I don't know what are they. Please be very specific and detail explaining what's going on about this issue. regards

              L Offline
              L Offline
              Lost User
              wrote on last edited by
              #6

              Hi, Yes, by setting the initial due time to zero the waitable timer becomes immediately signaled. When the timer becomes signaled an APC is queued on your thread. When an APC is queued on your thread the kernel object representing the timer object has its reference count incremented. When you call CancelWaitableTimer if an APC is queued on your thread... a single APC is dequeued and the reference count is decremented by one and the timer is NOT cancelled. Follow the instructions in my previous post to cancel your waitable timer. Other thoughts... You can achieve the one-time timer fire by simply setting the

              _In_ LONG lPeriod,

              argument to zero. Best Wishes, -David Delaune (MSFT)

              E 1 Reply Last reply
              0
              • L Lost User

                Hi, Yes, by setting the initial due time to zero the waitable timer becomes immediately signaled. When the timer becomes signaled an APC is queued on your thread. When an APC is queued on your thread the kernel object representing the timer object has its reference count incremented. When you call CancelWaitableTimer if an APC is queued on your thread... a single APC is dequeued and the reference count is decremented by one and the timer is NOT cancelled. Follow the instructions in my previous post to cancel your waitable timer. Other thoughts... You can achieve the one-time timer fire by simply setting the

                _In_ LONG lPeriod,

                argument to zero. Best Wishes, -David Delaune (MSFT)

                E Offline
                E Offline
                ehaerim
                wrote on last edited by
                #7

                Can you clearly explain why the code below triggers the timer twice from time to time, say once in 5-10 executions? MSDN does not explain this weird phenomenon at all.

                #if !defined(BTF)
                #define BTF(b) (b ? _T('T') : _T('F'))
                #endif

                #if !defined(ERR)
                #define ERR(b, d) (b ? 0 : d)
                #endif

                BOOL CWTApp::InitInstance()
                {
                CWinApp::InitInstance();

                HANDLE hTimer = CreateWaitableTimer( NULL, FALSE, NULL );

                BOOL bRet;
                DWORD dwErr;

                #define WAITABLETIMER_DUETIME_INITIALLY (0)
                #define WAITABLETIMER_PERIOD (USER_TIMER_MINIMUM * 200)
                #define SLEEP_BEFORE_CWT Sleep(50)

                LARGE_INTEGER DueTime;
                DueTime.QuadPart = WAITABLETIMER_DUETIME_INITIALLY;

                bRet = SetWaitableTimer(hTimer, &DueTime, WAITABLETIMER_PERIOD, NULL, NULL, FALSE);
                ATLTRACE(_T("SetWaitableTimer_1(hTimer)=%c/%d\n"), BTF(bRet), ERR(bRet, GetLastError()));

                dwErr = WaitForSingleObject(hTimer, INFINITE);
                ATLTRACE(_T("Timer(1) was signaled. dwErr=%d\n"), dwErr);

                SLEEP_BEFORE_CWT;
                bRet = CancelWaitableTimer(hTimer);
                ATLTRACE(_T("CancelWaitableTimer(hTimer)=%c\n"), BTF(bRet), ERR(bRet, GetLastError()));

                dwErr = WaitForSingleObject(hTimer, INFINITE);
                ATLTRACE(_T("Timer(2) was signaled. dwErr=%d\n"), dwErr);

                bRet = CloseHandle(hTimer);

                return FALSE;
                }

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • World
                • Users
                • Groups