> On Sep 6, 2017, at 3:07 PM, Taylor Swift <[email protected]> wrote:
> 
> 
> 
>> On Sep 6, 2017, at 4:31 PM, Joe Groff <[email protected]> wrote:
>> 
>> 
>>> On Sep 6, 2017, at 2:28 PM, Andrew Trick <[email protected]> wrote:
>>> 
>>> 
>>>>> On Sep 6, 2017, at 1:12 PM, Joe Groff via swift-evolution 
>>>>> <[email protected]> wrote:
>>>>> 
>>>>> 
>>>>> On Sep 6, 2017, at 1:06 PM, Taylor Swift <[email protected]> wrote:
>>>>> 
>>>>> 
>>>>> 
>>>>>> On Wed, Sep 6, 2017 at 12:41 PM, Joe Groff via swift-evolution 
>>>>>> <[email protected]> wrote:
>>>>>> Currently, memory is deallocated by an instance method on 
>>>>>> UnsafeMutablePointer, deallocate(count:). Like much of the Swift pointer 
>>>>>> API, performing this operation on a buffer pointer requires extracting 
>>>>>> baseAddress! and count. It is very common for the allocation code above 
>>>>>> to be immediately followed by:
>>>>>> 
>>>>>> defer
>>>>>> 
>>>>>> {
>>>>>>  buffer.
>>>>>> baseAddress?.deallocate(capacity: buffer.count
>>>>>> )
>>>>>> }
>>>>>> 
>>>>>> This method is extremely problematic because nearly all users, on first 
>>>>>> seeing the signature of deallocate(capacity:), will naturally conclude 
>>>>>> from the capacity label that deallocate(capacity:) is equivalent to some 
>>>>>> kind of realloc()that can only shrink the buffer. However this is not 
>>>>>> the actual behavior — deallocate(capacity:) actually ignores the 
>>>>>> capacity argument and just calls free() on self. The current API is not 
>>>>>> only awkward and suboptimal, it is misleading. You can write perfectly 
>>>>>> legal Swift code that shouldn’t segfault, but still can, for example
>>>>>> 
>>>>>> var ptr = UnsafeMutablePointer<UInt8>.allocate(capacity: 1000000
>>>>>> )
>>>>>> ptr.
>>>>>> initialize(to: 13, count: 1000000
>>>>>> )
>>>>>> ptr.
>>>>>> deallocate(capacity: 500000) // deallocate the second half of the memory 
>>>>>> block
>>>>>> ptr[0] // segmentation fault
>>>>>> where the first 500000 addresses should still be valid if the 
>>>>>> documentation is to be read literally.
>>>>> 
>>>>> The fact that the Swift runtime currently uses malloc/free is an 
>>>>> implementation detail. Tracking deallocation size is a very useful 
>>>>> optimization for better allocator backends, and C++ underwent an ABI 
>>>>> break to make it so that sized delete can be supported. Maybe we can 
>>>>> change the name to `deallocate(allocatedCapacity:)` to make it clear that 
>>>>> it isn't resizing the memory, and/or make the capacity argument optional 
>>>>> so that you can pay for the overhead of the allocator deriving the size 
>>>>> if it's inconvenient for the calling code to carry the size around, but 
>>>>> we shouldn't remove the functionality altogether.
>>>>> 
>>>>> -Joe
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected]
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>> 
>>>>> The idea is to get the house in order by removing all parameters from 
>>>>> deallocate(), since that’s what it really does right now. Then, in the 
>>>>> future, if Swift gets a more sophisticated allocator backend, a new 
>>>>> method like deallocate(capacity:) or reallocate(toCapacity:) could be 
>>>>> added without conflicting with the currently named deallocate(capacity:). 
>>>>> However, using the function signature to pretend that it does something 
>>>>> it can’t actually do right now is extremely dangerous.
>>>> 
>>>> I don't think that's a good idea in this case, because it's not unlikely 
>>>> we would explore an optimized allocator soon after ABI stability, and 
>>>> retrofitting these interfaces in a future version of Swift would put a 
>>>> deployment target limit on when they can be used, and mean that a lot of 
>>>> user code would need to be retrofitted to carry allocated capacities where 
>>>> needed to see any benefit.
>>>> 
>>>> -Joe
>>> 
>>> The fact that we’re using malloc and free is already part of the ABI 
>>> because old libraries need to be able to deallocate memory allocated by 
>>> newer libraries.
>> 
>> The compiler doesn't ever generate calls directly to malloc and free, and 
>> the runtime entry points we do use already take size and alignment on both 
>> allocation and deallocation.
>> 
>>> Within the standard library we could make use of some new deallocation fast 
>>> path in the future without worrying about backward deployment.
>>> 
>>> Outside of the standard library, clients will get the benefits of whatever 
>>> allocator is available on their deployed platform because we now encourage 
>>> them to use UnsafeBufferPointer.deallocate(). We can change the 
>>> implementation inside UnsafeBufferPointer all we want, as long as it’s 
>>> still malloc-compatible.
>>> 
>>> I’m sure we’ll want to provide a better allocation/deallocation API in the 
>>> future for systems programmers based on move-only types. That will already 
>>> be deployment-limited.
>>> 
>>> Absolute worst case, we introduce a sized UnsafePointer.deallocate in the 
>>> future. Any new code outside the stdlib that wants the performance 
>>> advantage would either need to
>>> - trivially wrap deallocation using UnsafeBufferPointer
>>> - create a trivial UnsafePointer.deallocate thunk under an availability flag
>> 
>> Since we already have sized deallocate, why would we take it away? If the 
>> name is misleading, we can change the name.
>> 
>> -Joe
> 
> Exactly, we are changing the name to `deallocate()`. As for the old 
> `deallocate(capacity:)` method that *needs* to be removed because it is 
> actively harmful. As I’ve explained it’s not enough to just drop in a sized 
> backend later as an “implementation detail” because it’s not an 
> implementation detail, you’re changing the fundamental behavior of that 
> method.

It would remove a mode of "holding it wrong", but the specified behavior will 
not change; it has and will always fully deallocate the object being referenced 
in the cases where the call has defined behavior. A sized implementation may 
fail more obviously when you violate the contract in the future. Not having 
sized deallocation is a known deficiency of the C model we've been fairly 
diligent about avoiding in Swift's allocation interfaces, and it would be 
extremely unfortunate for us to backpedal from it.

-Joe
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to