[swift-users] Postponing owned object deinitialization

Nate Chandler nathaniel.chandler at gmail.com
Mon Feb 27 12:34:55 CST 2017


Hi Ole,

A quick follow-up--are you suggesting calling withExtendedLifetime inside
the closure passed to async, as below?

class AsyncHandle {

    let handle: Handle

    deinit {
        let handle: Handle = self.handle
        q.async {
            withExtendedLifetime(handle) {}
        }
    }
}

If so, it seems that that may be a final answer to (3).  It does raise an
additional question, however:

(6) Would we expect calling withExtendedLifetime to have different behavior
from calling an @inline(never) function from a closure enqueued async from
deinit?

Thank you again,
Nate Chandler

On Mon, Feb 27, 2017 at 1:20 PM Nate Chandler <nathaniel.chandler at gmail.com>
wrote:

> Hi Ole,
>
> Thank you very much for your response.
>
> I don't believe withExtendedLifetime can help out here.  The trouble is
> that withExtendedLifetime only extends a lifetime until the end of a scope,
> synchronously.
>
> In my case, I want to extend the lifetime of Handle from AsyncHandle's
> deinit until a closure enqueued asynchronously (from AsyncHandle's deinit)
> is invoked.  Using withExtendedLifetime, I could only extend the lifetime
> of Handle until the end of AsyncHandle's deinit method.
>
> I want to write
>
> class AsyncHandle {
>
>     let handle: Handle
>
>     deinit {
>         let handle: Handle = self.handle
>         q.async {
>             letHandleDieNow(handle)
>         }
>     }
>
> }
>
> If I tried to use withExtendedLifetime, I could only write
>
> class AsyncHandle {
>
>     let handle: Handle
>
>     deinit {
>         let handle: Handle = self.handle
>         withExtendedLifetime(handle) { handle in
>             q.async {
>                 // This is where I want handle to finally be deinitialized
>             }
>         }
>         // This is where handle is actually deinitialized, prior
> (probably) to the invocation of the closure passed to async
>     }
>
> }
>
> Please let me know if I am misunderstanding your suggestion or the
> withExtendedLifetime API.
>
> Thank you very much,
> Nate Chandler
>
>
> On Mon, Feb 27, 2017 at 12:31 PM Ole Begemann <ole at oleb.net> wrote:
>
> On 27/02/2017 15:17, Nate Chandler via swift-users wrote:
> > Hello all,
> >
> > I'm encountering a behavior around object lifetime and deinitialization
> > that is surprising to me.  Here's an approximation of what I'm
> encountering:
> >
> > I have a class Handle whose deinit must execute on a certain queue
> > (let's call it q--in my case it is a global queue).  I have a second
> > class AsyncHandle which owns a Handle and needs to be able to deinit on
> > any queue.  There's a tension to resolve between the deinitialization
> > contracts of Handle and AsyncHandle.  To resolve it, in AsyncHandle's
> > deinit, I am binding the owned instance of Handle to a local variable,
> > dispatching async onto the q, and from the async closure, calling
> > extendLifetime with the Handle, where extendLifetime is the following:
> >
> > @inline(never) func extendLifetime<T : AnyObject>() {}
> >
> > AsyncHandle's deinit looks like
> >
> > deinit {
> >     let handle = self.handle
> >     q.async {
> >         extendLifetime(handle)
> >     }
> > }
> >
> > This approach seems to work--the Handle gets deinit on q, the
> > appropriate queue.  Most of the time.  In the debugger, there is never a
> > problem.  Occasionally and inconsistently, on some devices, I am,
> > however, seeing a behavior that _seems_ to be the synchronous
> > deallocation of Handle from AsyncHandle's deinit on the queue that
> > AsyncHandle happens to be deinitializing on (not q).  If that is indeed,
> > the behavior I'm seeing, I do not understand why it is happening.
> >
> > A few questions:
> > (1) Given an object B owned (exclusively--no other objects have
> > references to A) by an object A, is it legal to extend the lifetime of B
> > in the deinit of A?  (It might conceivably not be legal if there is a
> > rule such as the following: when the runtime observes that an object R
> > referenced by a field of a deinitializing object O has a reference count
> > of one, it assumes that R must die when O dies and consequently puts R
> > into some moribund state from which there is no revivification.)
> > (2) If it is legal, is calling extendLifetime from a dispatch async the
> > appropriate way to do it?
> > (3) Is my "implementation" (such as it is) of extendLifetime correct?
> >  (I can't use the stdlib's _fixLifetime, unfortunately, or even
> > implement extendLifetime in the same way.)
>
> Have you tried using the standard library's withExtendedLifetime(_:_:)
> function?
>
> > (4) Does optimization based on visibility enter into this?  In my case
> > the AsyncHandle is fileprivate.
> > (5) Is there any entirely different approach to satisfy the following
> > requirements?  (a) AsyncHandle be releasable/deinitializable on any
> > thread.  (b) Handle be deinitialized only on some dispatch queue q.  (c)
> > AsyncHandle has the only reference to Handle.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170227/7718c86e/attachment.html>


More information about the swift-users mailing list