{"id":10853,"date":"2011-04-22T07:00:00","date_gmt":"2011-04-22T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/04\/22\/even-if-you-have-a-lock-you-can-borrow-some-lock-free-techniques\/"},"modified":"2011-04-22T07:00:00","modified_gmt":"2011-04-22T07:00:00","slug":"even-if-you-have-a-lock-you-can-borrow-some-lock-free-techniques","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110422-00\/?p=10853","title":{"rendered":"Even if you have a lock, you can borrow some lock-free techniques"},"content":{"rendered":"<p>\nEven if you prefer to use a lock\n(after all, they are much easier to program),\nyou can borrow some lock-free techniques.\nFor example, consider this:\n<\/p>\n<pre>\nCRITICAL_SECTION g_cs;\nGORILLADATA g_data;\nvoid PokeGorilla(double intensity)\n{\n  EnterCriticalSection(&amp;g_cs);\n  DeformGorilla(intensity, &amp;g_data);\n  Reticulate(&amp;g_data.spline);\n  int stress = CalculateTension(&amp;g_data.spline);\n  if (stress &lt; 25)      g_data.mood = RELAXED;\n  else if (stress &lt; 50) g_data.mood = ANNOYED;\n  else                  g_data.mood = ANGRY;\n  DeleteObject(g_data.hbmGorilla);\n  g_data.hbmGorilla = RenderGorilla(&amp;g_data);\n  LeaveCriticalSection(&amp;g_cs);\n}\n<\/pre>\n<p>\nThere are some concerns here.\nFirst of all, there&#8217;s the lock hierarchy issue:\nIf reticulating a spline takes the geometry lock,\nthat may violate our lock hierarchy.\n<\/p>\n<p>\nIf the lock <code>g_cs<\/code> is a hot lock,\nyou may be concerned that all this gorilla stuff will\nhold the lock for too long.\nMaybe rendering a gorilla is a slow and complicated\noperation because it&#8217;s hard to get the fur just right.\n<\/p>\n<p>\nThese issues become less onerous if you switch to a lock-free\nalgorithm, but that&#8217;s an awful lot of work, and it&#8217;s hard to\nget right.\nBut maybe you can do just 20% of the work to get 80% of the benefit.\n<\/p>\n<p><pre>\nvoid PokeGorilla(double intensity)\n{\n  <font COLOR=\"blue\">\/\/ Capture\n  EnterCriticalSection(&amp;g_cs);\n  GORILLADATA data = g_data; \/\/ typo fixed\n  LeaveCriticalSection(&amp;g_cs);\n  \/\/ Recalculate based on captured data<\/font>\n  DeformGorilla(intensity, &amp;data);\n  Reticulate(&amp;data.spline);\n  int stress = CalculateTension(&amp;data.spline);\n  if (stress &lt; 25)      data.mood = RELAXED;\n  else if (stress &lt; 50) data.mood = ANNOYED;\n  else                  data.mood = ANGRY;\n  data.hbmGorilla = RenderGorilla(&amp;data);\n  <font COLOR=\"blue\">\/\/ Commit\n  EnterCriticalSection(&amp;g_cs);\n  HBITMAP hbmToDelete = g_data.hbmGorilla;\n  g_data = data;\n  LeaveCriticalSection(&amp;g_cs);<\/font>\n  DeleteObject(hbmToDelete);\n}\n<\/pre>\n<p>\nHere, we use the capture\/try\/commit model.\nWe capture the state of the gorilla into a local variable,\nthen perform our update based on that captured state.\nThe spline reticulation takes place without any locks held,\nwhich avoids introducing a lock hierarchy violation.\nAnd rendering the gorilla is done without any locks held,\nwhich avoids introducing a choke point on the lock.\nAfter the calculations are done, we then re-enter the lock\nand commit the changes.\n<\/p>\n<p>\nThis pattern uses a last-writer-wins model.\nIf another thread pokes the gorilla while we are still\ncalculating the previous gorilla state, we will overwrite\nthat gorilla state when we complete.\nFor some scenarios, that&#8217;s acceptable.\nBut maybe the gorilla&#8217;s emotional state needs to be an accumulation\nof all the times he&#8217;s ben poked.\nWe want to detect that somebody has poked the gorilla while we\nwere busy calculating so that we can incorporate that new information\ninto the final result.\n<\/p>\n<p>\nTo do that, we introduce\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/04\/12\/10152296.aspx\">\na change counter<\/a>.\n<\/p>\n<pre>\n<font COLOR=\"blue\">LONG g_lCounter;<\/font>\nvoid PokeGorilla(double intensity)\n{\n  <font COLOR=\"blue\">BOOL fSuccess;\n  do {<\/font>\n    \/\/ Capture\n    EnterCriticalSection(&amp;g_cs);\n    GORILLADATA data = g_data; \/\/ typo fixed\n    <font COLOR=\"blue\">LONG lCounter = g_lCounter;<\/font>\n    LeaveCriticalSection(&amp;g_cs);\n    \/\/ Recalculate based on captured data\n    DeformGorilla(intensity, &amp;data);\n    Reticulate(&amp;data.spline);\n    int stress = CalculateTension(&amp;data.spline);\n    if (stress &lt; 25)      data.mood = RELAXED;\n    else if (stress &lt; 50) data.mood = ANNOYED;\n    else                  data.mood = ANGRY;\n    data.hbmGorilla = RenderGorilla(&amp;data);\n    \/\/ Commit\n    EnterCriticalSection(&amp;g_cs);\n    HBITMAP hbmToDelete;\n    <font COLOR=\"blue\">if (lCounter == g_lCounter)\n    {<\/font>\n      hbmToDelete = g_data.hbmGorilla;\n      g_data = data;\n      <font COLOR=\"blue\">g_lCounter++;\n      fSuccess = TRUE;\n    } else {\n      hbmToDelete = data.hbmGorilla;\n      fSuccess = FALSE;\n    }<\/font>\n    LeaveCriticalSection(&amp;g_cs);\n    DeleteObject(hbmToDelete);\n  <font COLOR=\"blue\">} while (!fSuccess);<\/font>\n}\n<\/pre>\n<p>\nIn addition to the regular gorilla data,\nwe also associate a change counter that is incremented\neach time somebody pokes the gorilla.\nIn real life, you might want to make the change counter\npart of the <code>GORILLA&shy;DATA<\/code> structure.\n(Actually, in real life, you probably shouldn&#8217;t poke a gorilla.)\nIn a lock-free algorithm, we would\n<code>Interlocked&shy;Compare&shy;Exchange&shy;Release<\/code>\nthe lock counter to see if the lock counter changed\n(and if not, to update it with the new lock counter).\nBut since a <code>GORILLA&shy;DATA<\/code> structure\ncannot be updated atomically,\nwe have to use our critical section to perform the\ncomparison-and-update.\n<\/p>\n<p>\nEven though we used a lock, we still follow the lock-free pattern.\nIf the gorilla has been poked while we were busy processing our own poke,\nthen we throw away the results of our computations and start over,\nso that our poke can be accumulated with the previous pokes.\n<\/p>\n<p>\n<b>Exercise<\/b>:\nWhat constraints must be applied to the\n<code>GORILLADATA<\/code> structure for this technique to work?\n<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Even if you prefer to use a lock (after all, they are much easier to program), you can borrow some lock-free techniques. For example, consider this: CRITICAL_SECTION g_cs; GORILLADATA g_data; void PokeGorilla(double intensity) { EnterCriticalSection(&amp;g_cs); DeformGorilla(intensity, &amp;g_data); Reticulate(&amp;g_data.spline); int stress = CalculateTension(&amp;g_data.spline); if (stress &lt; 25) g_data.mood = RELAXED; else if (stress &lt; 50) g_data.mood [&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-10853","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Even if you prefer to use a lock (after all, they are much easier to program), you can borrow some lock-free techniques. For example, consider this: CRITICAL_SECTION g_cs; GORILLADATA g_data; void PokeGorilla(double intensity) { EnterCriticalSection(&amp;g_cs); DeformGorilla(intensity, &amp;g_data); Reticulate(&amp;g_data.spline); int stress = CalculateTension(&amp;g_data.spline); if (stress &lt; 25) g_data.mood = RELAXED; else if (stress &lt; 50) g_data.mood [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10853","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=10853"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10853\/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=10853"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=10853"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=10853"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}