{"id":14243,"date":"2010-04-26T07:00:00","date_gmt":"2010-04-26T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/04\/26\/why-doesnt-tryentercriticalsection-try-harder\/"},"modified":"2010-04-26T07:00:00","modified_gmt":"2010-04-26T07:00:00","slug":"why-doesnt-tryentercriticalsection-try-harder","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100426-00\/?p=14243","title":{"rendered":"Why doesn&#039;t TryEnterCriticalSection try harder?"},"content":{"rendered":"<p>\nBart wants to know\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/pages\/407234.aspx#1317239\">\nwhy the <code>Try&shy;Enter&shy;Critical&shy;Section<\/code>\ngives up if the critical\nsection is busy instead of trying the number of times specified by\nthe critical section spin count<\/a>.\n<\/p>\n<p>\nActually, there was another condition on the proposed new behavior:\n&#8220;but does not release its timeslice\nto the OS if it fails to get it while spinning.&#8221;\nThis second condition is a non-starter because you can&#8217;t prevent\nthe operating system from taking your timeslice away from you.\nThe best you can do is detect that you lost your previous\ntimeslice when you receive the next one.\nAnd even that is expensive: You have to keep watching the CPU cycle\ncounter, and if it jumps by too much, then you lost your timeslice.\n(And you might have lost it due to a hardware interrupt or paging.\nGood luck stopping those from happening.)\n<\/p>\n<p>\nEven if there were a cheap way of detecting that the operating system\nwas about to take your timeslice away from you, what good would it do?\n&#8220;Oh, my calculations indicate that if I spin one more time,\nI will lose my timeslice, so I&#8217;ll just fail and return.&#8221;\nNow the application regains control with 2 instructions left\nin its timeslice.\nThat&#8217;s not even enough time to test the return value and take\na conditional jump!\nEven if the <code>Try&shy;Enter&shy;Critical&shy;Section<\/code> managed to\nreturn just before the timeslice expired, that&#8217;s hardly any\nconsolation, because the timeslice is going to expire\nbefore the application can react to it.\nWhatever purpose there was to &#8220;up to the point where you&#8217;re\nabout to release the timeslice&#8221;\nis lost.\n<\/p>\n<p>\nOkay, maybe the intention of that clause was\n&#8220;without intentionally releasing its timeslice\n(but if it loses its timeslice in the normal course of events,\nwell that&#8217;s the way the cookie crumbles).&#8221;\nThat brings us back to the original question.\nWhy doesn&#8217;t <code>Try&shy;Enter&shy;Critical&shy;Section<\/code> try harder?\nWell, because if it tried harder, then the people who didn&#8217;t want\nit to try hard at all would complain that it tried too hard.\n<\/p>\n<p>\nThe function <code>Try&shy;Enter&shy;Critical&shy;Section<\/code> may have\nbeen ambiguously named, because it doesn&#8217;t describe <i>how hard<\/i>\nthe function should try.\nThough in general, functions named <code>TryXxx<\/code> try only once,\nand that&#8217;s the number of times\n<code>Try&shy;Enter&shy;Critical&shy;Section<\/code> tries.\nPerhaps a clearer (but bulkier name) would have been\n<code>Enter&shy;Critical&shy;Section&shy;If&shy;Not&shy;Owned&shy;By&shy;Another&shy;Thread<\/code>.\n<\/p>\n<p>\nThe <code>Try&shy;Enter&shy;Critical&shy;Section<\/code> function\nrepresents the core of the\n<code>Enter&shy;Critical&shy;Section<\/code> function.\nIn pseudocode, the two functions work like this:\n<\/p>\n<pre>\nBOOL TryEnterCriticalSection(CRITICAL_SECTION *CriticalSection)\n{\n  atomically {\n   if (CriticalSection is free or is owned by the current thread) {\n     claim the critical section and return TRUE;\n   }\n  }\n  return FALSE;\n}\nvoid EnterCriticalSection(CRITICAL_SECTION *CriticalSection)\n{\n for (;;) {\n  DWORD SpinTimes = 0;\n  do {\n    if (TryEnterCriticalSection(CriticalSection)) return;\n  } while (++SpinTimes &lt; GetSpinCount(CriticalSection));\n  WaitForCriticalSectionOwnerToLeave(CriticalSection);\n }\n}\n<\/pre>\n<p>\nThe <code>Try&shy;Enter&shy;Critical&shy;Section<\/code> function represents\nthe smallest meaningful part of the <code>Enter&shy;Critical&shy;Section<\/code>\nprocess.\nIf you want it to spin, you can write your own\n<code>Try&shy;Enter&shy;Critical&shy;Section&shy;With&shy;Spin&shy;Count<\/code>\nfunction:\n<\/p>\n<pre>\nBOOL TryEnterCriticalSectionWithSpinCount(\n    CRITICAL_SECTION *CriticalSection,\n    DWORD SpinCount)\n{\n  DWORD SpinTimes = 0;\n  do {\n    if (TryEnterCriticalSection(CriticalSection)) return TRUE;\n  } while (++SpinTimes &lt; SpinCount);\n  return FALSE;\n}\n<\/pre>\n<p>\n(Unfortunately, there is no\n<code>Get&shy;Critical&shy;Section&shy;Spin&shy;Count<\/code>\nfunction, so you&#8217;ll just have to keep track of it yourself.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bart wants to know why the Try&shy;Enter&shy;Critical&shy;Section gives up if the critical section is busy instead of trying the number of times specified by the critical section spin count. Actually, there was another condition on the proposed new behavior: &#8220;but does not release its timeslice to the OS if it fails to get it while [&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":[26],"class_list":["post-14243","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Bart wants to know why the Try&shy;Enter&shy;Critical&shy;Section gives up if the critical section is busy instead of trying the number of times specified by the critical section spin count. Actually, there was another condition on the proposed new behavior: &#8220;but does not release its timeslice to the OS if it fails to get it while [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/14243","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=14243"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/14243\/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=14243"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=14243"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=14243"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}