{"id":108740,"date":"2023-09-08T07:00:00","date_gmt":"2023-09-08T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108740"},"modified":"2023-09-08T07:11:46","modified_gmt":"2023-09-08T14:11:46","slug":"20230908-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230908-00\/?p=108740","title":{"rendered":"On transferring or copying ABI pointers between smart pointers"},"content":{"rendered":"<p>A customer traced a crash to a reference count underflow. But they were using smart pointers. I thought smart pointers avoided reference count problems.<\/p>\n<p>Smart pointers make it easier to avoid reference count problems, but you still have to use them correctly.<\/p>\n<p>There are some basic operations for ABI interop with smart pointers.<\/p>\n<ul>\n<li>Take ownership: Given a raw pointer, accept the pointer and assume responsibility for releasing it. This typically goes by the name &#8220;attach&#8221;.<\/li>\n<li>Share ownership: Given a raw pointer, accept the pointer and share ownership by incrementing the reference count. This doesn&#8217;t have a standard name, but I&#8217;ll call it &#8220;copy&#8221; for the purpose of this discussion.<\/li>\n<li>Non-destructive access: Given a smart pointer, produce the raw pointer without relinquishing ownership. This is typically called &#8220;get&#8221;.<\/li>\n<li>Abandon ownership: Given a smart pointer, produce the raw pointer and relinquish ownership. This is typically called &#8220;detach&#8221;.<\/li>\n<\/ul>\n<p>It is important that when you move raw pointers between smart pointers, that you match up the two semantics: If you want to transfer ownership, then the donor should use detach semantics and the recipient should use attach semantics. If you want to share ownership, then the donor should use get semantics and the recipient should use copy semantics.<\/p>\n<p>If you mess up, then you end up with reference count bugs: If the donor detaches but the recipient copies, then you have a reference count leak. If the donor offers with &#8220;get&#8221; but the recipient attaches, then you have an over-release.<\/p>\n<p>Here are some tables showing various Windows smart pointer libraries and how they express the two pairs of operations. Note that this table is just an overview; consult the corresponding documentation for further information. For example, some of the methods require that the smart pointer be non-null.<\/p>\n<p>First, the attach\/detach (transfer) operations:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Library<\/th>\n<th>Detach (donor)<\/th>\n<th>Attach (recipient)<\/th>\n<\/tr>\n<tr>\n<td>_com_ptr_t<\/td>\n<td valign=\"baseline\"><code>sp.Detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.Attach(p)<\/code><br \/>\n<code>_com_ptr&lt;T&gt;(p, false)<\/code><\/td>\n<\/tr>\n<tr>\n<td>ATL (CComPtr)<\/td>\n<td valign=\"baseline\"><code>sp.Detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.Attach(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>MFC (IPTR)<\/td>\n<td valign=\"baseline\"><code>sp.Detach()<br \/>\n\/* Note 1 *\/<\/code><\/td>\n<td valign=\"baseline\"><code>sp.Attach(p)<\/code><br \/>\n<code>sp.Attach(p, FALSE)<\/code><br \/>\n<code>IPTR(T)(p, FALSE)<\/code><\/td>\n<\/tr>\n<tr>\n<td>WRL<\/td>\n<td valign=\"baseline\"><code>sp.Detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.Attach(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>wil (com_ptr)<\/td>\n<td valign=\"baseline\"><code>sp.detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.attach(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/WinRT (com_ptr)<\/td>\n<td valign=\"baseline\"><code>sp.detach()<\/code><br \/>\n<code>detach_abi(sp)<\/code><\/td>\n<td valign=\"baseline\"><code>sp.attach(p)<\/code><br \/>\n<code>attach_abi(sp, p)<\/code><br \/>\n<code>com_ptr&lt;T&gt;(p, take_ownership_from_abi)<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note 1: IPTR&#8217;s <code>Detach()<\/code> method does not return the raw pointer.<\/p>\n<p>And then the get\/copy (share) operations:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Library<\/th>\n<th>Get (donor)<\/th>\n<th>Copy (recipient)<\/th>\n<\/tr>\n<tr>\n<td>_com_ptr_t<\/td>\n<td valign=\"baseline\"><code>static_cast&lt;T*&gt;(sp)<\/code><br \/>\n<code>sp.GetInterfacePtr()<\/code><\/td>\n<td valign=\"baseline\"><code>sp = p<\/code><br \/>\n<code>sp.Attach(p, true)<\/code><br \/>\n<code>_com_ptr&lt;T&gt;(p, true)<\/code><\/td>\n<\/tr>\n<tr>\n<td>ATL (CComPtr)<\/td>\n<td valign=\"baseline\"><code>static_cast&lt;T*&gt;(sp)<\/code><br \/>\n<code>*sp<\/code><br \/>\n<code>sp.p<\/code><\/td>\n<td valign=\"baseline\"><code>sp = p<\/code><br \/>\n<code>CComPtr&lt;T&gt;(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>MFC (IPTR)<\/td>\n<td valign=\"baseline\"><code>static_cast&lt;T*&gt;(sp)<\/code><br \/>\n<code>sp.GetInterfacePtr()<\/code><\/td>\n<td valign=\"baseline\"><code>sp = p<\/code><br \/>\n<code>sp.Attach(p, TRUE)<\/code><br \/>\n<code>IPTR(T)(p)<\/code><br \/>\n<code>IPTR(T)(p, TRUE)<\/code><\/td>\n<\/tr>\n<tr>\n<td>WRL<\/td>\n<td valign=\"baseline\"><code>sp.Get()<\/code><\/td>\n<td valign=\"baseline\"><code>sp = p<\/code><br \/>\n<code>ComPtr&lt;T&gt;(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>wil (com_ptr)<\/td>\n<td valign=\"baseline\"><code>sp.get()<\/code><\/td>\n<td valign=\"baseline\"><code>sp = p<\/code><br \/>\n<code>com_ptr&lt;T&gt;(p)<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/WinRT (com_ptr)<\/td>\n<td valign=\"baseline\"><code>sp.get()<\/code><br \/>\n<code>get_abi(sp)<\/code><\/td>\n<td valign=\"baseline\"><code>sp.copy_from(p)<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Of course, if you are remaining within one row of the table, then you can usually avoid having to operate through ABI pointers. For example, you can just use <code>sp1 = sp2<\/code> to copy one smart pointer to another smart pointer of the same type, or <code>sp1 = std::move(sp2)<\/code> to transfer ownership. The purpose of the above tables is to help you move between rows: The donor and recipient should both be attach\/detach (transfer) semantics, or they should both be get\/copy (share) semantics.<\/p>\n<p>But wait, there&#8217;s another option, which I will call the &#8220;recipient&#8221; pattern.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Library<\/th>\n<th>Create recipient<\/th>\n<th>Transfer to<br \/>\nrecipient<\/th>\n<th>Copy to<br \/>\nrecipient<\/th>\n<\/tr>\n<tr>\n<td>_com_ptr_t<\/td>\n<td valign=\"baseline\"><code>&amp;sp<\/code><\/td>\n<td valign=\"baseline\"><code>*r = sp.Detach()<\/code><\/td>\n<td valign=\"baseline\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>ATL (CComPtr)<\/td>\n<td valign=\"baseline\"><code>&amp;sp<\/code><br \/>\n<code>&amp;sp.p<\/code><\/td>\n<td valign=\"baseline\"><code>*r = sp.Detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.CopyTo(r)<\/code><\/td>\n<\/tr>\n<tr>\n<td>MFC (IPTR)<\/td>\n<td valign=\"baseline\"><code>&amp;sp<\/code><\/td>\n<td valign=\"baseline\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>WRL<\/td>\n<td valign=\"baseline\"><code>&amp;sp<\/code><br \/>\n<code>sp.GetAddressOf()<\/code><br \/>\n<code>sp.ReleaseAndGetAddressOf()<\/code><\/td>\n<td valign=\"baseline\"><code>*r = sp.Detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.CopyTo(r)<\/code><\/td>\n<\/tr>\n<tr>\n<td>wil (com_ptr)<\/td>\n<td valign=\"baseline\"><code>&amp;sp<\/code><br \/>\n<code>sp.addressof()<\/code><br \/>\n<code>sp.put()<\/code><br \/>\n<code>sp.put_void()<\/code><\/td>\n<td valign=\"baseline\"><code>*r = sp.detach()<\/code><\/td>\n<td valign=\"baseline\"><code>sp.query_to(r)<\/code><br \/>\n<code>sp.copy_to(r)<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/WinRT (com_ptr)<\/td>\n<td valign=\"baseline\"><code>sp.put()<\/code><br \/>\n<code>sp.put_void()<\/code><br \/>\n<code>put_abi(sp)<\/code><\/td>\n<td valign=\"baseline\"><code>*r = sp.detach()<\/code><br \/>\n<code>*r = detach_abi(sp)<\/code><\/td>\n<td valign=\"baseline\"><code>sp.copy_to(r)<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The &#8220;recipient&#8221; pattern produces a <code>T**<\/code>, and it&#8217;s up to the donor to decide whether to transfer or copy ownership to it. This pattern is used by most of COM: For example, <code>Create\u00adStream\u00adOn\u00adHGlobal<\/code> takes a recipient as its final parameter, and it puts a reference to the newly-created stream in that recipient. You as the caller don&#8217;t know or care whether the function copied or transferred a reference to the stream into your recipient pointer; all that you care about is that when the function returns, your recipient pointer received a reference to the thing.<\/p>\n<p><b>Bonus chatter<\/b>: C++ <code>shared_ptr<\/code> and <code>unique_ptr<\/code> have similar patterns and pitfalls. For example, given the declarations <code>unique_ptr&lt;T&gt; u1, u2;<\/code>, you shouldn&#8217;t write things like <code>u1.reset(u2.get())<\/code> or <code>std::shared_ptr&lt;int&gt;(u1.get());<\/code> since they result in double-ownership and therefore will eventually result in double-destruction.<\/p>\n<p><b>Bonus reading<\/b>: <a title=\"We're using a smart pointer, so we can't possibly be the source of the leak\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20091119-00\/?p=15963\"> We&#8217;re using a smart pointer, so we can&#8217;t possibly be the source of the leak<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Keep track of who owns the reference.<\/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-108740","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Keep track of who owns the reference.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108740","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=108740"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108740\/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=108740"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108740"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108740"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}