[swift-evolution] Should Swift apply "statement scope" for ARC

Michael Gottesman mgottesman at apple.com
Thu Sep 22 17:57:14 CDT 2016


> On Sep 21, 2016, at 4:01 PM, John Holdsworth via swift-evolution <swift-evolution at swift.org> wrote:
> 
> My contrived example was a bit flimsy. I’d better unpack the full story. The real
> code I had problems with was based around the following Java instance wrapper:
> 
> open class JNIObject: JNIObjectProtocol {
> 
>     var _javaObject: jobject?
> 
>     open var javaObject: jobject? {
>         get {
>             return _javaObject
>         }
>         set(newValue) {
>             if newValue != _javaObject {
>                 let oldValue = _javaObject
>                 if newValue != nil {
>                     _javaObject = JNI.api.NewGlobalRef( JNI.env, newValue )
>                 }
>                 else {
>                     _javaObject = nil
>                 }
>                 if oldValue != nil {
>                     JNI.api.DeleteGlobalRef( JNI.env, oldValue )
>                 }
>             }
>         }
>     }
> 
>     deinit {
>         javaObject = nil
>     }
> 
> As a result the following transfer of a Java instance always worked:
> 
>     init(imageProducer:ImageProducer) {
>         let supr = CanvasBase()
>         super.init( javaObject: supr.javaObject )
>         image = createImage(imageProducer)
>     }
> 
> But the following only worked for debug compiles:
> 
>     init(imageProducer:ImageProducer) {
>         super.init( javaObject: CanvasBase().javaObject )
>         image = createImage(imageProducer)
>     }
> 
> Felt like a bit of a bear trap is all. Statement scope would avoid problems like this.

You are thinking about this the inverse way. That the first case works is an artifact of the optimizer failing to do a good enough job. Future improved ARC optimization can cause both to fail.

> 
> John
> 
> 
>> On 21 Sep 2016, at 23:34, Joe Groff <jgroff at apple.com <mailto:jgroff at apple.com>> wrote:
>> 
>> 
>>> On Sep 21, 2016, at 3:14 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> I haven't used it myself, but is this the use case addressed by `withExtendedLifetime(_:_:)`?
>> 
>> Yeah, if you want to vend resources managed by an object to consumers outside of that object like this, you need to use withExtendedLifetime to keep the object alive for as long as you're using the resources. A cleaner way to model this might be to put the class or protocol in control of handling the I/O to the file handle, instead of vending the file handle itself, so that the ownership semantics fall out more naturally:
>> 
>> protocol Storage {
>>  func write(bytes: UnsafeRawPointer, count: Int)
>> }
>> 
>> func save(string: String, to: Storage?) {
>>    if let data = string.data(using: String.Encoding.utf8) {
>>        data.withUnsafeBytes {
>>            _ = to?.write(bytes: $0, count: data.count)
>>        }
>>    }
>> }
>> 
>> -Joe
>> 
>>> On Wed, Sep 21, 2016 at 16:54 John Holdsworth via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> Hi,
>>> 
>>> For complex statements in C++ any temporary instances created in the course
>>> of an expression have their lifetime extended to the completion of the current
>>> statement after which they are all deallocated en masse. This makes certain
>>> types of language usage possible and easier to reason with.
>>> 
>>> I’m bringing this up as I had a problem with some code crashing only when
>>> compiled with release configuration and the problem could have been avoided
>>> if Swift deferred deallocation to the end of a statement. While Swift’s ARC policy
>>> is consistent in itself this seems to be a particular problem interfacing between
>>> language/reference counting systems. My problem code was a Java-Swift Bridge.
>>> 
>>> A contrived example:
>>> 
>>> import Foundation
>>> 
>>> protocol Storage {
>>>    var fp: UnsafeMutablePointer<FILE> { get }
>>> }
>>> 
>>> class FileStorage: Storage {
>>> 
>>>    let fp: UnsafeMutablePointer<FILE>
>>> 
>>>    init?(path: String, mode: String = "w") {
>>>        print("Opening")
>>>        let fp = fopen(path, mode)
>>>        if fp == nil {
>>>            return nil
>>>        }
>>>        self.fp = fp!
>>>    }
>>> 
>>>    deinit {
>>>        print("Closing")
>>>        fclose(fp)
>>>    }
>>> }
>>> 
>>> func save(string: String, to: Storage?) {
>>>    if let data = string.data(using: String.Encoding.utf8) {
>>>        print("Saving1")
>>>        if let fp = to?.fp {
>>>            print("Saving2")
>>>            data.withUnsafeBytes {
>>>                _ = fwrite($0, 1, data.count, fp)
>>>            }
>>>            print("Saving3")
>>>        }
>>>    }
>>> }
>>> 
>>> save(string: "Hello World\n", to: FileStorage(path: "/tmp/a.txt"))
>>> 
>>> 
>>> In debug configuration is prints:
>>> Opening
>>> Saving1
>>> Saving2
>>> Saving3
>>> Closing
>>> 
>>> Whereas in release configuration it prints:
>>> Opening
>>> Saving1
>>> Closing <!!!
>>> Saving2
>>> Saving3
>>> 
>>> The optimiser is vigorously deallocating objects when they are no longer referenced regardless
>>> of whether an variable referencing it is still in scope (In fairness this particular problem only occurs
>>> for Optional augments of Protocols) but this behaviour seems to be implicit in the current language
>>> spec. The alternative is to retain arguments themselves as I believe they are in Objective-C ARC.
>>> 
>>> This would have been avoided if the temporary FileStorage instance has been considered to have
>>> a lifetime up to the end of the statement calling function save() and hence the duration of the call.
>>> This needed increase ARC overhead in any way. Just alter the timing of it to be more conservative.
>>> 
>>> John
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160922/ed057757/attachment.html>


More information about the swift-evolution mailing list