{"id":110809,"date":"2025-01-27T07:00:00","date_gmt":"2025-01-27T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110809"},"modified":"2025-01-27T11:01:26","modified_gmt":"2025-01-27T19:01:26","slug":"20250127-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250127-00\/?p=110809","title":{"rendered":"A pattern for obtaining a single value while holding a lock"},"content":{"rendered":"<p>It is often the case that you have a mutex or other lockable object which protects access to a complex variable, and you want to read the variable&#8217;s value (possibly modifying it in the process) while holding the lock, but then operate on the value outside the lock.<\/p>\n<p>The traditional way is to do something like this:<\/p>\n<pre>\/\/ Assume we have these member variables\r\nstd::mutex m_mutex;\r\nWidget m_widget;\r\n\r\n\/\/ Get a copy of the widget\r\nWidget widget;\r\n{\r\n    auto guard = std::lock_guard(m_mutex);\r\n    widget = m_widget;\r\n}\r\n\u27e6 use the variable \"widget\" \u27e7\r\n<\/pre>\n<p>This does suffer from the problem of running the <code>Widget<\/code> constructor for an object that we&#8217;re going to overwrite anyway. The compiler will also have to deal with the possibility that the <code>lock_<wbr \/>guard<\/code> constructor throws an exception, forcing the destruction of a freshly-constructed <code>Widget<\/code>.<\/p>\n<p>I like to use this alternate pattern, using an immediately-invoked lambda that returns a value.<\/p>\n<pre>\/\/ Get a copy of the widget\r\nWidget widget = [&amp;] {\r\n    auto guard = std::lock_guard(m_mutex);\r\n    return m_widget;\r\n}();\r\n\u27e6 use the variable \"widget\" \u27e7\r\n<\/pre>\n<p>Thanks to the magic of copy elision (in this specific form known as Return Value Optimization, or RVO), we never have to construct a dummy Widget. Instead, the <code>widget<\/code> variable directly receives a copy of <code>m_widget<\/code> under the lock, but the value survives the lock.<\/p>\n<p>This also can be used to move a value out of a lock-protected object, so that the value can destruct outside the lock.<\/p>\n<pre>\/\/ Move the widget into our variable\r\nWidget widget = [&amp;] {\r\n    auto guard = std::lock_guard(m_mutex);\r\n    return std::move(m_widget);\r\n}();\r\n\/\/ Allow the widget to destruct outside the lock\r\n<\/pre>\n<p>Or to exchange the value in the object while under the lock and operate on the old value outside the lock.<\/p>\n<pre>Widget widget = [&amp;] {\r\n    auto guard = std::lock_guard(m_mutex);\r\n    return std::exchange(m_widget, {});\r\n}();\r\n\/\/ Allow the widget to destruct outside the lock\r\n<\/pre>\n<p>If you do this more than a few times, you may want to write a helper.<\/p>\n<pre>Widget CopySavedWidget()\r\n{\r\n    auto guard = std::lock_guard(m_mutex);\r\n    return m_widget;\r\n}\r\n\r\ntemplate&lt;typename T&gt;\r\nWidget ExchangeSavedWidget(T&amp;&amp; value)\r\n{\r\n    auto guard = std::lock_guard(m_mutex);\r\n    return std::exchange(m_widget, std::forward&lt;T&gt;(value)):\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The immediately-invoked lambda that returns a value.<\/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-110809","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The immediately-invoked lambda that returns a value.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110809","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=110809"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110809\/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=110809"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110809"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110809"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}