{"id":104839,"date":"2021-02-10T07:03:06","date_gmt":"2021-02-10T15:03:06","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104839"},"modified":"2021-02-10T07:03:06","modified_gmt":"2021-02-10T15:03:06","slug":"20210210-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210210-06\/?p=104839","title":{"rendered":"The COM static store, part 3: Avoiding creation of an expensive temporary when setting a singleton"},"content":{"rendered":"<p>Last time, we looked at one way to <a title=\"The\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210209-00\/?p=104835\"> avoid a race condition when initializing a singleton in the COM static store<\/a>. But it did create the possibility of creating an object that might be thrown away, and that could be a problem if the object is expensive to construct, or if other circumstances prevent you from creating more than one of those objects.<\/p>\n<p>In that case, you can expand the lock to cover the construction of the <code>Thing<\/code>, and construct it only if you&#8217;re sure you&#8217;re going to need it.<\/p>\n<pre>Thing GetSingletonThing()\r\n{\r\n    auto props = CoreApplication::Properties();\r\n    if (auto found = props.TryLookup(L\"Thing\")) {\r\n        return found.as&lt;Thing&gt;();\r\n    }\r\n    auto guard = std::lock_guard(m_lock);\r\n    if (auto found = props.TryLookup(L\"Thing\")) {\r\n        return found.as&lt;Thing&gt;();\r\n    }\r\n    <span style=\"color: blue;\">auto thing = MakeAThing();<\/span>\r\n    props.Insert(L\"Thing\", thing);\r\n    return thing;\r\n}\r\n<\/pre>\n<p>This avoids the creation of a throwaway <code>Thing<\/code>, but it does come at a cost: Since the <code>Thing<\/code> is created under the lock, its constructor is at risk of deadlocking if it acquires its own locks or performs cross-thread operations.<\/p>\n<p>Suppose there&#8217;s another lock <var>L<\/var>, and the caller of <code>Get\u00adSingleton\u00adThing<\/code> owns that lock. The <code>Get\u00adSingleton\u00adThing<\/code> function sees that there is no <code>Thing<\/code> yet, so it takes its own private lock, and then constructs a new <code>Thing<\/code>. If the <code>Thing<\/code> constructor also attempts to acquire lock <var>L<\/var>, and the lock <var>L<\/var> is non-recursive, then you have recursive acquisition of <var>L<\/var>, which is formally undefined behavior.<\/p>\n<p>Even if the lock <var>L<\/var> allows recursive acquisition, you can still deadlock:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border-right: solid 1px black;\">Thread 1<\/th>\n<th>Thread 2<\/th>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Acquire lock <var>L<\/var><\/td>\n<td>Call <code>Get\u00adSingleton\u00adThing<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Call <code>Get\u00adSingleton\u00adThing<\/code><\/td>\n<td>Object doesn&#8217;t exist yet<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Object doesn&#8217;t exist yet<\/td>\n<td>Acquire lock <var>m_lock<\/var><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Wait for lock <var>m_lock<\/var><\/td>\n<td>Object still doesn&#8217;t exist yet<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">\u00a0<\/td>\n<td>Construct a new <code>Thing<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">\u00a0<\/td>\n<td>Wait for lock <var>L<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Here we hit a classic deadlock, where each thread holds one lock and is waiting for the other one.<\/p>\n<p>But even if there is no lock <var>L<\/var>, you can still run into problems if the construction of <code>Thing<\/code> requires cross-thread operations.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th style=\"border-right: solid 1px black;\">Thread 1<\/th>\n<th>Thread 2<\/th>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">\u00a0<\/td>\n<td>Call <code>Get\u00adSingleton\u00adThing<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Call <code>Get\u00adSingleton\u00adThing<\/code><\/td>\n<td>Object doesn&#8217;t exist yet<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Object doesn&#8217;t exist yet<\/td>\n<td>Acquire lock <var>m_lock<\/var><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">Wait for lock <var>m_lock<\/var><\/td>\n<td>Object still doesn&#8217;t exist yet<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">\u00a0<\/td>\n<td>Construct a new <code>Thing<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: solid 1px black;\">\u00a0<\/td>\n<td>Send a request to Thread 1 to do some work<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This time, Thread 2 is waiting for Thread 1 to do some work so it can finish constructing the <code>Thing<\/code>, but Thread 1 cannot do that work because it is waiting for the lock that protects <code>Thing<\/code> construction.<\/p>\n<p>I&#8217;ve seen all of these types of deadlocks in production code. They hit rarely, but when they do, everybody has a bad day. Resolving the problem can be complicated because the locks or cross-thread operations are deeply embedded in the architecture, and a lot of refactoring has to be done to avoid dangerous operations while holding a lock.<\/p>\n<p>So yeah, be extremely mindful about what you do while holding a lock. Don&#8217;t call out to foreign code while holding a lock.<\/p>\n<p>Okay, enough about deadlocks. We&#8217;ll look at some ways of optimizing the COM singleton pattern next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Don&#8217;t create one unless you really need one.<\/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-104839","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Don&#8217;t create one unless you really need one.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104839","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=104839"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104839\/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=104839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}