{"id":10983,"date":"2011-04-08T07:00:01","date_gmt":"2011-04-08T14:00:01","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/04\/08\/patterns-for-using-the-initonce-functions\/"},"modified":"2011-04-08T07:00:01","modified_gmt":"2011-04-08T14:00:01","slug":"patterns-for-using-the-initonce-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110408-01\/?p=10983","title":{"rendered":"Patterns for using the InitOnce functions"},"content":{"rendered":"<p>\nSince writing lock-free code is is\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/04\/07\/10150728.aspx#10151021\">\nsuch a headache-inducer<\/a>,\nyou&#8217;re probably best off making some other people suffer the headaches\nfor you.\nAnd those other people are the kernel folks, who have developed\nquite a few lock-free building blocks so you don&#8217;t have to.\nFor example, there&#8217;s a collection of functions for manipulating\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms684121(VS.85).aspx\">\ninterlocked lists<\/a>.\nBut today we&#8217;re going to look at the\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa363808\">\none-time initialization<\/a> functions.\n<\/p>\n<p>\nThe simplest version of the one-time initialization functions\nisn&#8217;t actually lock-free,\nbut it does implement the double-checked-lock pattern for you\nso you don&#8217;t have to worry about the details.\nThe usage pattern for the\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683493(v=VS.85).aspx\">\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> function<\/a>\nis pretty simple.\nHere it is in its simplest form:\n<\/p>\n<pre>\nint SomeGlobalInteger;\nBOOL CALLBACK ThisRunsAtMostOnce(\n    PINIT_ONCE initOnce,\n    PVOID Parameter,\n    PVOID *Context)\n{\n    calculate_an_integer(&amp;SomeGlobalInteger);\n    return TRUE;\n}\nvoid InitializeThatGlobalInteger()\n{\n    static INIT_ONCE initOnce = INIT_ONCE_STATIC_INIT;\n    InitOnceExecuteOnce(&amp;initOnce,\n                        ThisRunsAtMostOnce,\n                        nullptr, nullptr);\n}\n<\/pre>\n<p>\nIn the simplest form, you give\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> an\n<code>INIT_ONCE<\/code> structure (where it records\nits state),\nand a callback.\nIf this is the first time that\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> is called\nfor a particular\n<code>INIT_ONCE<\/code> structure,\nit calls the callback.\nThe callback can do whatever it likes,\nbut presumably it&#8217;s doing some one-time initialization.\nIf another thread calls\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> on the same\n<code>INIT_ONCE<\/code> structure,\nthat other thread will wait until the first thread is finished\nits one-time execution.\n<\/p>\n<p>\nWe can make this a tiny bit fancier by supposing that\nthe calculation of the integer can fail.\n<\/p>\n<pre>\nBOOL CALLBACK ThisSucceedsAtMostOnce(\n    PINIT_ONCE initOnce,\n    PVOID Parameter,\n    PVOID *Context)\n{\n    return SUCCEEDED(calculate_an_integer(&amp;SomeGlobalInteger));\n}\nBOOL TryToInitializeThatGlobalInteger()\n{\n    static INIT_ONCE initOnce = INIT_ONCE_STATIC_INIT;\n    return InitOnceExecuteOnce(&amp;initOnce,\n                               ThisSucceedsAtMostOnce,\n                               nullptr, nullptr);\n}\n<\/pre>\n<p>\nIf your initialization function returns <code>FALSE<\/code>,\nthen the initialization is considered to have failed,\nand the next time somebody calls\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code>,\nit will try to initialize again.\n<\/p>\n<p>\nA slightly fancier use of the\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> function\ntakes advantage of the <code>Context<\/code> parameter.\nThe kernel folks noticed that an\n<code>INIT_ONCE<\/code> structure in the &#8220;initialized&#8221;\nstate has a lot of unused bits,\nand they&#8217;ve offered to let you use them.\nThis is convenient if the thing you&#8217;re initializing is a pointer\nto a C++ object, because it means that there&#8217;s only one thing\nyou need to worry about instead of two.\n<\/p>\n<pre>\nBOOL CALLBACK AllocateAndInitializeTheThing(\n    PINIT_ONCE initOnce,\n    PVOID Parameter,\n    PVOID *Context)\n{\n    *Context = new(nothrow) Thing();\n    return *Context != nullptr;\n}\nThing *GetSingletonThing(int arg1, int arg2)\n{\n    static INIT_ONCE initOnce = INIT_ONCE_STATIC_INIT;\n    void *Result;\n    if (InitOnceExecuteOnce(&amp;initOnce,\n                            AllocateAndInitializeTheThing,\n                            nullptr, &amp;Result))\n    {\n        return static_cast&lt;Thing*&gt;(Result);\n    }\n    return nullptr;\n}\n<\/pre>\n<p>\nThe final parameter to\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> function\nreceives the magic almost-pointer-sized data that the function\nwill remember for you.\nYour callback function passes this magic value back through\nthe <code>Context<\/code> parameter,\nand then\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> gives it\nback to you as the <code>Result<\/code>.\n<\/p>\n<p>\nAs before, if two threads call\n<code>Init&shy;Once&shy;Execute&shy;Once<\/code> simultaneously\non an uninitialized <code>INIT_ONCE<\/code> structure,\none of them will call the initialization function and the other will wait.\n<\/p>\n<p>\nUp until now, we&#8217;ve been looking at the synchronous initialization\npatterns.\nThey aren&#8217;t lock-free:\nIf you call <code>Init&shy;Once&shy;Execute&shy;Once<\/code>\nand initialization of the the <code>INIT_ONCE<\/code> structure is\nalready in progress, your call will wait until that initialization\nattempt completes (either successfully or unsuccessfully).\n<\/p>\n<p>\nMore interesting is the asynchronous pattern.\nHere it is, as applied to our <code>Singleton&shy;Manager<\/code> exercise:\n<\/p>\n<pre>\n SingletonManager(const SINGLETONINFO *rgsi, UINT csi)\n               : m_rgsi(rgsi), m_csi(csi),\n                 m_rgio(new INITONCE[csi]) {\n   for (UINT iio = 0; iio &lt; csi; iio++) {\n    InitOnceInitialize(&amp;m_rgio[iio]);\n   }\n }\n ...\n \/\/ Array that describes objects we&#039;ve created\n \/\/ runs parallel to m_rgsi\n INIT_ONCE *m_rgio;\n};\nITEMCONTROLLER *SingletonManager::Lookup(DWORD dwId)\n{\n ... same as before until we reach the &quot;singleton constructor pattern&quot;\n void *pv = NULL;\n BOOL fPending;\n if (!InitOnceBeginInitialize(&amp;m_rgio[i], INIT_ONCE_ASYNC,\n                              &amp;fPending, &amp;pv)) return NULL;\n if (fPending) {\n  ITEMCONTROLLER *pic = m_rgsi[i].pfnCreateController();\n  if (pic &amp;&amp; InitOnceComplete(&amp;m_rgio[i],\n                              INIT_ONCE_ASYNC, pic)) {\n   pv = pic;\n  } else {\n   \/\/ lost the race - discard ours and retrieve the winner\n   delete pic;\n   InitOnceBeginInitialize(&amp;m_rgio[i], INIT_ONCE_CHECK_ONLY,\n                           &amp;fPending, &amp;pv);\n  }\n }\n return static_cast&lt;ITEMCONTROLLER *&gt;(pv);\n}\n<\/pre>\n<p>\nThe pattern for asynchronous initialization is as follows:\n<\/p>\n<ul>\n<li>Call <code>Init&shy;Once&shy;Begin&shy;Initialize<\/code>\n    in async mode.<\/p>\n<li>If it returns <code>fPending == FALSE<\/code>,\n    then initialization has already been performed and you can\n    go ahead and use the result passed back in the final parameter.<\/p>\n<li>Otherwise, initialization is pending.\n    Do your initialization, but remember that since this is a lock-free\n    algorithm, there can be many threads trying to initialize\n    simultaneously, so you have to be careful\n    <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/04\/07\/10150728.aspx\">\n    how you manipulate global state<\/a>.\n    This pattern works best if initialization takes the form of\n    creating a new object (because that means multiple threads\n    performining initialization are each creating independent objects).<\/p>\n<li>If you successfully created the object,\n    call <code>Init&shy;Once&shy;Complete<\/code> with the result\n    of your initialization.<\/p>\n<li>If <code>Init&shy;Once&shy;Complete<\/code> succeeds,\n    then you won the initialization race, and you&#8217;re done.<\/p>\n<li>If <code>Init&shy;Once&shy;Complete<\/code> fails,\n    then you lost the initialization race and should clean up your\n    failed initialization.\n    In that case, you should call\n    <code>Init&shy;Once&shy;Begin&shy;Initialize<\/code>\n    one last time to get the answer from the winner.\n<\/ul>\n<p>\nit&#8217;s conceptually simple; it just takes a while to explain.\nbut at least now it&#8217;s in recipe form.\n<\/p>\n<p>\n<b>Exercise<\/b>: Instead of calling\n<code>Init&shy;Once&shy;Complete<\/code> with\n<code>INIT_ONCE_INIT_FAILED<\/code>,\nwhat happens if the function simply returns\nwithout ever completing the init-once?\n<\/p>\n<p>\n<b>Exercise<\/b>:\nWhat happens if two threads try to perform\nasynchronous initialization and the first one\nto complete fails?\n<\/p>\n<p>\n<b>Exercise<\/b>:\nCombine the results of the first two exercises\nand draw a conclusion.\n<\/p>\n<p>\n<b>Update<\/b>:\nI got it wrong in the case of a failed asynchronous\ninitialization.\nYou&#8217;re just supposed to abandon the initialization\nrather than report failure.\nThe code and discussion have been updated.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Letting the smart people do the work for you.<\/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-10983","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Letting the smart people do the work for you.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10983","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=10983"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10983\/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=10983"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=10983"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=10983"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}