简体   繁体   English

Winapi Timer回调线程,永不返回

[英]Winapi Timer callback thread, never returns

I've got to debug some code which is not from me. 我必须调试一些不是我写的代码。 This code implement a timer API using winapi Timer interface. 此代码使用winapi Timer接口实现计时器API。 I'm not very used to this Winapi functionality, so i could use your help :) 我不太习惯使用Winapi功能,因此可以使用您的帮助:)

From what I understand this code is done like this : 据我了解,这段代码是这样完成的:

=> Init() =>初始化()

timerQueue = CreateTimerQueue();

=> CreateTimer() => CreateTimer()

CreateTimerQueueTimer(timerHandle, timerQueue, timerCallback, ..., WT_EXECUTEDEFAULT);

=> timerCallback() => timerCallback()

 DeleteTimerQueueTimer(timerQueue , timerHandle,  NULL));
 calback() //Launch user-defined callback

=> CleanUp() // to be called at the end => CleanUp()//最终被调​​用

DeleteTimerQueueEx(timerQueue , INVALID_HANDLE_VALUE);

When we test that, user-defined callback are executed successfully after the desired amount of time. 当我们测试时,用户定义的回调将在所需的时间后成功执行。 But after that timerCallback threads keep pending and never return, preventing the all process to returns. 但是之后,timerCallback线程将保持挂起状态,并且永不返回,从而阻止所有进程返回。 Using VS debugger I can see those threads (named TppWorkerThread@4) on the thread... 使用VS调试器,我可以在线程上看到那些线程(名为TppWorkerThread @ 4)。

Perhaps we miss something to make callback returns properly or we created some sort of deadlocks... However I cannot figure it out ... 也许我们错过了一些使回调函数正确返回的东西,或者我们创建了某种死锁……但是我无法弄清楚……

Please let me know if I forgot some relevant information. 如果我忘记了一些相关信息,请告诉我。

Thank you for your help. 谢谢您的帮助。

EDIT: 编辑:
Further information : 更多信息 :
- Blocking thread are at this state at the end of the process : -进程结束时,阻塞线程处于此状态:
* Category :Worker Thread *类别:工人线
* Name : _TppWorkerThread@4 *名称:_TppWorkerThread @ 4
* Location : _ZwWaitForWorkViaWorkerFactory@8 *位置:_ZwWaitForWorkViaWorkerFactory @ 8
* Priotity : Normal *优先级:正常

EDIT2: Having some more time to work on that strange behavior, I am now able to reproduce it in a standalone code. EDIT2:有更多的时间来处理这种奇怪的行为,我现在能够在独立的代码中重现它。

#include <windows.h>
#include <stdio.h>

HANDLE gDoneEvent;
HANDLE hTimer[5];
HANDLE hTimerQueue = NULL;
HANDLE g_threadHandle;

void PeriodicCallback(void)
{
  printf("Periodic routine called.\n");
}

void SingleCallback(void)
{
  printf("Single routine called.\n");
  if (!DeleteTimerQueueTimer(hTimerQueue, hTimer[2], NULL))
    printf("DeleteTimerQueueTimer() fail. Return value is %d.\n", GetLastError());
}

void CALLBACK CommonCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
  printf("Common routine called. Parameter is %d.\n", *(int *)lpParam);
  ((void (*)(void))lpParam)();

}

void MainTest(void)
{
  // Use an event object to track the TimerRoutine execution
  gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (NULL == gDoneEvent)
  {
    printf("CreateEvent failed (%d)\n", GetLastError());
    return -1;
  }

  if(0 == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL))
  {
    printf("SetThreadPriority failed (%d)\n", GetLastError());
    return -2;
  }

  // Create the timer queue.
  hTimerQueue = CreateTimerQueue();
  if (NULL == hTimerQueue)
  {
    printf("CreateTimerQueue failed (%d)\n", GetLastError());
    return -3;
  }

/*
  if (!CreateTimerQueueTimer( &hTimer[2], hTimerQueue, 
    (WAITORTIMERCALLBACK)CommonCallback, &SingleCallback, 1000, 0, WT_EXECUTEDEFAULT))
  {
    printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
    return -4;
  }
*/

  if (!CreateTimerQueueTimer( &hTimer[4], hTimerQueue, 
    (WAITORTIMERCALLBACK)CommonCallback, &PeriodicCallback, 10, 500, WT_EXECUTEDEFAULT))
  {
    printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
    return -5;
  }

  // TODO: Do other useful work here 

  printf("Call timer routine in 10 seconds...\n");

  Sleep(4000);

  CloseHandle(gDoneEvent);

  if (!DeleteTimerQueueTimer(hTimerQueue, hTimer[4],  INVALID_HANDLE_VALUE))
    printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError());
  // Delete all timers in the timer queue.
  if (!DeleteTimerQueueEx(hTimerQueue, INVALID_HANDLE_VALUE))
    printf("DeleteTimerQueue failed (%d)\n", GetLastError());

  Sleep(1000);

  ExitThread(0);
}

int main(int argc, char **argv[])
{
  if(g_threadHandle == CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainTest, NULL, 0, NULL))
    printf("Creation fail");

  ExitThread(0);
}

I'm compiling this code on VisualStudio 2010 Professional. 我正在Visual Studio 2010 Professional上编译此代码。

It appears that event after calling DeleteTimerQueueTimer() some threads remain pending on the threads pool, preventing my process to shutdown. 似乎在调用DeleteTimerQueueTimer()之后,某些线程在线程池中仍处于挂起状态,从而阻止了我的进程关闭。 I still cannot figured it out ... 我仍然不知道...

When you call DeleteTimerQueueEx with an INVALID_HANDLE_VALUE as its second parameter it will block until all callbacks which are running have completed. 当您使用INVALID_HANDLE_VALUE作为其第二个参数调用DeleteTimerQueueEx时,它将阻塞,直到所有正在运行的回调都完成为止。 The Error may be in one of your callback functions which never returns. 错误可能在您的回调函数之一中,该函数永远不会返回。

You are calling DeleteTimerQueueTimer(timerQueue , timerHandle, NULL); 您正在调用DeleteTimerQueueTimer(timerQueue , timerHandle, NULL); with NULL as the third parameter, this will not wait for the callback to complete if one is running at the time you delete the timer. 如果使用NULL作为第三个参数,则在删除计时器时,如果正在运行回调,它将不会等待回调完成。 I suggest using DeleteTimerQueueTimer(timerQueue , timerHandle, INVALID_HANDLE_VALUE ) which will block until the call back completes (if one is running). 我建议使用DeleteTimerQueueTimer(timerQueue , timerHandle, INVALID_HANDLE_VALUE ),它将阻塞直到回调完成(如果正在运行)。 Calling cleanUp() without using the blocking version of DeleteTimerQueueTimer is likely a bug as you may be cleaning up at the same time as the callback is executing. 在不使用DeleteTimerQueueTimer的阻止版本的情况下调用cleanUp()可能是一个错误,因为您可能在执行回调的同时进行清理。

It could also be a problem of calling DeleteTimerQueueEx or DeleteTimerQueueTimer from within a callback, which is forbidden. 从禁止的回调中调用DeleteTimerQueueExDeleteTimerQueueTimer也可能是一个问题。 Break on execution of DeleteTimerQueueEx and look at what thread you are in, if its a TppWorkerThread than you have found your bug. 中断DeleteTimerQueueEx的执行,并查看您所在的线程(如果它的TppWorkerThread大于您发现的错误)。

EDIT: 编辑:

In your comment you say you do call DeleteTimerQueueTimer from within the callback but don't use INVALID_HANDLE_VALUE , reading the documentation again from http://msdn.microsoft.com/en-us/library/windows/desktop/ms682569%28v=vs.85%29.aspx this does seem to be legal but I distinctly remember us making design decisions to avoid this, I'm sorry this is so vague, I hope someone can give authoritative advice on this. 在您的评论中,您说您确实从回调中调用DeleteTimerQueueTimer ,但不使用INVALID_HANDLE_VALUE ,而是从http://msdn.microsoft.com/zh-cn/library/windows/desktop/ms682569%28v=vs重新阅读文档.85%29.aspx,这似乎是合法的,但我清楚地记得我们为避免这种情况而做出设计决策,对不起,这是如此含糊,我希望有人能对此提供权威建议。

We send an event/message to the queue of a non timer thread which then removes the timer, you could even have a dedicated thread for this but that is probably overkill. 我们将事件/消息发送到非计时器线程的队列中,然后该计时器删除计时器,您甚至可以为此设置一个专用线程,但这可能会过大。 At the end of the day you need to be sure that the timer is removed before doing cleanup so you have to either block on removal or have some other thread do it upon signaling of an event. 在一天结束时,您需要确保在执行清理之前已将计时器删除,因此您必须在删除时阻塞,或者在发出事件信号时让其他线程执行此操作。

After some work on that issue I think I got to an answer. 在该问题上进行了一些工作之后,我认为我得到了答案。 I appears that this timerQueue API is coded on top of threadPool winAPI, and when we ask to create a timerQueue Windows create a thread pool from where all callback will be launched. 我似乎将此timerQueue API编码在threadPool winAPI之上,并且当我们要求创建一个timerQueue Windows时,将创建一个线程池,从该线程池将启动所有回调。 Until here no problem, but, when we ask for timerQueue deletion, it appears that this thread pool is not deleted... This result in some thread keeping pending waiting to be used and preventing the process to returns. 直到这里没有问题,但是,当我们要求删除timerQueue时,似乎未删除此线程池...这导致某些线程保持等待使用状态,并阻止进程返回。

After some time (timeout??) those threads returns and the process exit. 一段时间后(超时?),这些线程返回并且进程退出。

I don't really get why this pool is not closed... but, now, I use a workaround : 我真的不明白为什么这个池没有关闭...但是,现在,我使用一种解决方法:

  exit(0);

At the end of my program, it's a bit brutal but it does the job (ie: killing my process, whatever threads are still pending or not) 在我的程序结束时,这有点残酷,但是可以完成工作(即:杀死我的进程,无论线程是否挂起)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM