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.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to