{"id":12663,"date":"2010-10-01T07:00:00","date_gmt":"2010-10-01T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/10\/01\/non-psychic-debugging-why-youre-leaking-timers\/"},"modified":"2010-10-01T07:00:00","modified_gmt":"2010-10-01T07:00:00","slug":"non-psychic-debugging-why-youre-leaking-timers","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20101001-00\/?p=12663","title":{"rendered":"Non-psychic debugging: Why you&#039;re leaking timers"},"content":{"rendered":"<p>\nI was not involved in this debugging puzzle,\nbut I was informed of its conclusions,\nand I think it illustrates both the process of debugging\nas well as uncovering a common type of defect.\nI&#8217;ve written it up in the style of a post-mortem.\n<\/p>\n<blockquote CLASS=\"m\">\n<p>\nA user reported that if they press and hold the F2 key\nfor about a minute,\nour program eventually stops working.\nAccording to Task Manager, our User object count has reached\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2007\/07\/18\/3926581.aspx\">\nthe 10,000 object limit<\/a>,\nand closer inspection revealed that we had created over 9000 timer\nobjects.\n<\/p>\n<p>\nWe ran the debugger and\nset breakpoints on <code>SetTimer<\/code> and <code>KillTimer<\/code>\nto print to the debugger each timer ID as it was created and destroyed.\nVisual inspection of the output revealed that all but one of the IDs\nbeing created was matched with an appropriate destruction.\nWe re-ran the scenario with a conditional breakpoint on\n<code>SetTimer<\/code> set to fire when that bad ID was set.\nIt didn&#8217;t take long for that breakpoint to fire,\nand we discovered that we were setting the timer against a\n<code>NULL<\/code> window handle.\n<\/p>\n<p>\nA different developer on the team arrived at the same\nconclusion by a different route.\nInstead of watching timers being created and destroyed,\nthe developer dumped each timer message before it was dispatched\nand observed that most of the\nentries were associated with <code>NULL<\/code> window handles.\n<\/p>\n<p>\nTwo independent analyses came to the same conclusion:\nWe were creating a bunch of thread timers and not destroying them.\n<\/p>\n<p>\nA closer inspection of the code revealed that\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2008\/10\/16\/9001218.aspx\">\nthread timers were not intended in the first place<\/a>.\nEach time the user presses F2,\nthe code calls <code>SetTimer<\/code> and passes a window handle\nit believes to be non-<code>NULL<\/code>.\nThe timer is destroyed\nin the window procedure&#8217;s <code>WM_TIMER<\/code> handler, but\nsince the timer was registered against the wrong window handle,\nthe <code>WM_TIMER<\/code> is never received by the intended\ntarget&#8217;s window procedure,\nand the timer is never destroyed.\n<\/p>\n<p>\nThe window handle is <code>NULL<\/code> due to a defect in the code\nwhich handles the F2 keypress:\nThe handle that the code wanted to use\nfor the timer had not yet been set.\n(It was set by a later step of F2 processing.)\nThe timer was being set by a helper function which is called both\nbefore and after the code that sets the handle, but it obviously was\nwritten on the assumption that it would only be called after.\n<\/p>\n<p>\nTo reduce the likelihood of this type of defect being introduced\nin the future,\nwe&#8217;re going to introduce a wrapper function around <code>SetTimer<\/code>\nwhich asserts that the window handle is non-<code>NULL<\/code> before\ncalling <code>SetTimer<\/code>.\n(In the rare case that we actually want a thread timer,\nwe&#8217;ll have a second wrapper function called\n<code>SetThreadTimer<\/code>.)\n<\/p>\n<\/blockquote>\n<p>\nI haven&#8217;t seen the wrapper function, but I suspect it goes\nsomething like this:\n<\/p>\n<pre>\ninline UINT_PTR SetWindowTimer(\n    __in HWND hWnd, \/\/ NB - not optional\n    __in UINT_PTR nIDEvent,\n    __in UINT uElapse,\n    __in_opt TIMERPROC lpTimerFunc)\n{\n    assert(hWnd != NULL);\n    return SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc);\n}\ninline UINT_PTR SetThreadTimer(\n    __in UINT uElapse,\n    __in_opt TIMERPROC lpTimerFunc)\n{\n    return SetTimer(NULL, 0, uElapse, lpTimerFunc);\n}\n__declspec(deprecated)\nWINUSERAPI\nUINT_PTR\nWINAPI\nSetTimer(\n    __in_opt HWND hWnd,\n    __in UINT_PTR nIDEvent,\n    __in UINT uElapse,\n    __in_opt TIMERPROC lpTimerFunc);\n<\/pre>\n<p>\nThere are few interesting things here.\n<\/p>\n<p>\nFirst, observe that the annotation for the first parameter to\n<code>SetWindowTimer<\/code> is <code>__in<\/code> rather than\n<code>__in_opt<\/code>. This indicates that the parameter\ncannot be <code>NULL<\/code>.\nCode analysis tools can use this information to attempt to identify\npotential defects.\n<\/p>\n<p>\nSecond, observe that the <code>SetThreadTimer<\/code> wrapper\nfunction omits the first two parameters.\nFor thread timers, the <code>hWnd<\/code> passed to\n<code>SetTimer<\/code> is always <code>NULL<\/code> and the\n<code>nIDEvent<\/code> is ignored.\n<\/p>\n<p>\nThird, after the two wrapper functions, we redeclare\nthe <code>SetTimer<\/code>, but mark it as <code>deprecated<\/code>\nso the compiler will complain if somebody tries to call the\noriginal function instead of one of the two wrappers.\n(The <code>__declspec(deprecated)<\/code> extended attribute\nis a nonstandard Microsoft extension.)\n<\/p>\n<p>\n<b>Exercise<\/b>:\nWhy did I use <code>__declspec(deprecated)<\/code> instead of\n<code>#pragma deprecated(SetTimer)<\/code>?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was not involved in this debugging puzzle, but I was informed of its conclusions, and I think it illustrates both the process of debugging as well as uncovering a common type of defect. I&#8217;ve written it up in the style of a post-mortem. A user reported that if they press and hold the F2 [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-12663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>I was not involved in this debugging puzzle, but I was informed of its conclusions, and I think it illustrates both the process of debugging as well as uncovering a common type of defect. I&#8217;ve written it up in the style of a post-mortem. A user reported that if they press and hold the F2 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=12663"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12663\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=12663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=12663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=12663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}