Hi Arnold, This seems like a great optimization tool. The only question I have is about the method name. With the name `withUnsafeGuaranteedValue` we’re moving the term “guaranteed” into API in addition to it being an implementation detail of the compiler as far as I could tell. Is “guaranteed" the best name for this?
- Alex > On Mar 15, 2016, at 6:01 PM, Arnold Schwaighofer via swift-evolution > <[email protected]> wrote: > > > Updated proposal: use a method that accepts a closure to delineate the > required guaranteed lifetime for which the assertion by the programmer holds. > And, hopefully, no optimizer language speak. > > > Add an API to Unmanaged to get the instance it holds @guaranteed > > Proposal: SE-0000 Add an API to Unmanaged to get the instance it holds > guaranteed > <https://github.com/aschwaighofer/swift-evolution/blob/unmanaged_unsafe_guaranteed_proposal/proposals/0000-unsafe-guaranteed.md> > Author(s): Arnold Schwaighofer <https://github.com/aschwaighofer> > Status: Draft > Review manager: TBD > > <https://github.com/aschwaighofer/swift-evolution/blob/unmanaged_unsafe_guaranteed_proposal/proposals/0000-unsafe-guaranteed.md#introduction>Introduction > > The standard library Unmanged<Instance> struct provides an instance wrapper > that does not participate in ARC; it allows the user to make manual > retain/release calls and get at the contained instance at balanced/unbalanced > retain counts. > > This proposal suggests to add another method withUnsafeGuaranteedValue to > Unmanaged that accepts a closure. Calling this method is akin to making an > assertion about the guaranteed lifetime of the instance for the delinated > scope of the method invocation. The closure is passed the unmanaged reference > as a managed reference. > > func doSomething(u : Unmanged<Owned>) { > // The programmer asserts that there exists another managed reference of > the > // unmanaged reference stored in 'u' and that the lifetime of the > referenced > // instance is guaranteed to extend beyond the 'withUnsafeGuaranteedValue' > // invocation. > u.withUnsafeGuaranteedValue { > $0.doSomething() > } > } > This assertion will help the compiler remove ARC operations. > > public struct Unmanaged<Instance : AnyObject> { > > // Get the value of the unmanaged referenced as a managed reference without > // consuming an unbalanced retain of it and pass it to the closure. Asserts > // that there is some other reference ('the owning reference') to the > // unmanaged reference that guarantees the lifetime of the unmanaged > reference > // for the duration of the 'withUnsafeGuaranteedValue' call. > // > // NOTE: You are responsible for ensuring this by making the owning > reference's > // lifetime fixed for the duration of the 'withUnsafeGuaranteedValue' call. > // > // Violation of this will incur undefined behavior. > // > // A lifetime of a reference 'the instance' is fixed over a point in the > // programm if: > // > // * There is another managed reference to the same instance 'the instance' > // whose life time is fixed over the point in the program by means of > // 'withExtendedLifetime' closing over this point. > // > // var owningReference = Instance() > // ... > // withExtendedLifetime(owningReference) { > // point($0) > // } > // > // Or if: > // > // * There is a class, or struct instance ('owner') whose lifetime is fixed > at > // the point and which has a stored property that references 'the > instance' > // for the duration of the fixed lifetime of the 'owner'. > // > // class Owned { > // } > // > // class Owner { > // final var owned : Owned > // > // func foo() { > // withExtendedLifetime(self) { > // doSomething(...) > // } // Assuming: No stores to owned occur for the dynamic lifetime > of > // // the withExtendedLifetime invocation. > // } > // > // func doSomething() { > // // both 'self' and 'owned''s lifetime is fixed over this point. > // point(self, owned) > // } > // } > // > // The last rule applies transitively through a chains of stored references > // and nested structs. > // > // Examples: > // > // var owningReference = Instance() > // ... > // withExtendedLifetime(owningReference) { > // let u = Unmanaged.passUnretained(owningReference) > // for i in 0 ..< 100 { > // u.withUnsafeGuaranteedValue { > // $0.doSomething() > // } > // } > // } > // > // class Owner { > // final var owned : Owned > // > // func foo() { > // withExtendedLifetime(self) { > // doSomething(Unmanaged.passUnretained(owned)) > // } > // } > // > // func doSomething(u : Unmanged<Owned>) { > // u.withUnsafeGuaranteedValue { > // $0.doSomething() > // } > // } > // } > public func withUnsafeGuaranteedValue<Result>( > @noescape closure: (Instance) throws -> Result > ) rethrows { > let instance = _value > let (guaranteedInstance, token) = Builtin.unsafeGuaranteed(instance) > try closure(guaranteedInstance) > Builtin.unsafeGuaranteedEnd(token) > } > Prototype: link to a prototype implementation > <https://github.com/aschwaighofer/swift/tree/unsafe_guaranteed_prototype> > > <https://github.com/aschwaighofer/swift-evolution/blob/unmanaged_unsafe_guaranteed_proposal/proposals/0000-unsafe-guaranteed.md#motivation>Motivation > > A user can often make assertions that an instance is kept alive by another > reference to it for the duration of some scope. > > Consider the following example: > > class Owned { > func doSomeWork() {} > } > > public class Owner { > var ref: Owned > > init() { ref = Owned() } > > public func doWork() { > withExtendedLifetime(self) { > doSomething(ref) > } > } > > func doSomething(o: Owned) { > o.doSomeWork() > } > } > In this context the lifetime of Owner always exceeds o: Ownee. However, there > is currently no way to tell the compiler about such an assertion. We can pass > reference counted values in an Unmanged container without incurring reference > count changes. > > public class Owner { > > public func doWork() { > // No reference count for passing the parameter. > doSomething(Unmanaged.passUnretained(ref)) > } > > func doSomething(u: Unmanaged<Owned>) { > // ... > } > } > We can get at the contained instance by means of takeUnretainedValue() which > will return a balanced retained value: the value is returned at +1 for > release by the caller. However, when it comes to accessing the contained > instance we incur reference counting. > > func doSomething(u: Unmanaged<Owned>) { > // Incurs refcount increment before the call and decrement after for self. > u.takeUnretainedValue().doSomeWork() > } > With the proposed API call the user could make the assertion that u's > contained instance's lifetime is guaranteed by another reference to it. > > func doSomething(u: Unmanaged<Owned>) { > // Incurs refcount increment before the call and decrement after for self > // that can be removed by the compiler based on the assertion made. > u.withUnsafeGuaranteedValue { > $0.doSomeWork() > } > } > The compiler can easily remove the reference counts marked by this method > call with local reasoning. > > > <https://github.com/aschwaighofer/swift-evolution/blob/unmanaged_unsafe_guaranteed_proposal/proposals/0000-unsafe-guaranteed.md#impact-on-existing-code>Impact > on existing code > > This is a new API that does not replace an existing method. No existing code > should be affected. > > > <https://github.com/aschwaighofer/swift-evolution/blob/unmanaged_unsafe_guaranteed_proposal/proposals/0000-unsafe-guaranteed.md#alternatives-considered>Alternatives > considered > > A somewhat related proposal would be to allow for class types to completely > opt out of ARC forcing the programmer to perform manual reference counting. > The scope of such a proposal would be much bigger making it questionable for > Swift 3 inclusion. I believe that the two approaches are complementary and > don't completely supplant each other. This proposal's approach allows for > selectively opting out of ARC while providing a high notational burdon when > wide spread over the application (wrapping and unwrapping of Unmanaged). > Opting completely out of ARC is an all-in solution, which on the other hand > does not have the high notational burden. > > >> On Mar 15, 2016, at 4:06 PM, Dave Abrahams <[email protected] >> <mailto:[email protected]>> wrote: >> >> >> on Mon Mar 14 2016, John McCall <rjmccall-AT-apple.com >> <http://rjmccall-at-apple.com/>> wrote: >> >>>> In this example this would be for the lifetime for the value in x which >>>> extends to the lifetime of y. >>> >>> What if I return it or assign it to non-local memory? >>> >>> I feel like you’re trying to define this by optimizer behavior. >>> That’s not a workable language rule; programmers are not going to >>> reason about SSA values. >> >> Thank you, John. That is the problem I usually have with evaluating >> most optimizer proposals. > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
