Swift Unsafemutablepointer: Must I Call Deinitialize Before Deallocate

Swift 4.1 deinitialize and deallocate(capacity:) deprecated

Yes, that is part of SE-0184 Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size,
which has been implemented in Swift 4.1.

In particular:

Removing capacity from deallocate(capacity:) will end the confusion over what deallocate() does, making it obvious that deallocate() will free the entire memory block at self, just as if free() were called on it.

The old deallocate(capacity:) method should be marked as deprecated and eventually removed since it currently encourages dangerously incorrect code.

Should I call free if I am getting an UnsafeMutablePointer in Swift?

Yes, because of the name of the method. class_copyPropertyList includes the word "copy" which indicates that the buffer belongs to you. Note that the documentation also indicates this:

You must free the array with free().

So you should use free() to destroy this buffer. This is currently identical to calling .deallocate(), but that is not promised, so follow the instructions as given.

(Thanks to MartinR for running down the question of free vs deallocate.)

Accessing deallocated memory with UnsafeMutablePointer

@jckarter answered the question for me on Twitter.

So first of all it's always good to use the address sanitizer when working with memory directly.

Accessing memory after it's deallocated is undefined behavior.
So multiple things can happen we can not rely on this.

The behavior for this is because of:

When you free memory it isn't necessarily returned to the OS right
away, since it's more efficient to leave the memory as is until it's
needed for a new allocation. So you may be able to access an old value
some time after the memory is freed, but you can't rely on it.

Using memory checking tools like Address Sanitizer or Guard Malloc
should make the behavior more predictable, since they add more
checking for memory used after deallocation

-- @jckarter

Description when address sanitizer enabled:

let storage = UnsafeMutablePointer<Bool>.allocate(capacity: 1)
// initialize
storage.initialize(to: false)

storage.deinitialize(count: 1)
storage.deallocate()

print(storage)
print(storage.pointee) // Thread 1: Use of deallocated memory ❗️

What does `UnsafeMutablePointer.initialize()`actually do?

One reason you need initialize(), and the only one as for now maybe, is

for ARC.

You'd better think with local scope variables, when seeing how ARC works:

func test() {
var refVar: RefType = initValue //<-(1)
//...
refVar = newValue //<-(2)
//...
//<-(3) just before exiting the loacl scope
}

For a usual assignment as (2), Swift generates some code like this:

swift_retain(_newValue)
swift_release(_refVar)
_refVar = _newValue

(Assume _refVar and _newValue are unmanaged pseudo vars.)

Retain means incrementing the reference count by 1, and release means decrementing the reference count by 1.


But, think what happens when the initial value assignment as at (1).

If the usual assignment code was generated, the code might crash at this line:

swift_release(_refVar)

because newly allocated region for a var may be filled with garbages, so swift_release(_refVar) cannot be safely executed.

Filling the newly region with zero (null) and release safely ignoring the null could be one solution, but it's sort of redundant and not effective.

So, Swift generates this sort of code for initial value assignment:

(for already retained values, if you know ownership model, owned by you.)

_refVar = _initValue

(for unretained values, meaning you have no ownership yet.)

swift_retain(_initValue)
_refVar = _initValue

This is initialize.

No-releasing the garbage data, and assign an initial value, retaining it if needed.

(The above explanation of "usual assignment" is a little bit simplified, Swift omits swift_retain(_newValue) when not needed.)


When exiting the local scope at (3), Swift just generates this sort of code:

swift_release(_refVar)

So, this is deinitialize.


Of course, you know retaining and releasing are not needed for primitive types like Int, so initialize and deinitialize may be donothing for such types.

And when you define a value type which includes some reference type properties, Swift generates initialize and deinitialize procedures specialized for the type.


The local scope example works for the regions allocated on the stack, and initialize() and deinitialize() of UnsafeMutablePointer works for the regions allocated in the heap.

And Swift is evolving so swift, that you might find another reason for needing initialize() and deinitialize() in the future, you'd better make it a habit to initialize() and deinitialize() all allocated UnsafeMutablePointers of any Pointee types.

deinitialize() was obsoleted in swift 5.0

deinitialize now requires a count parameter indicating how many values you want to deinitialise.

From the context, the code is probably trying to deinitialise everything the pointer references, so the number of values we deinitialise will be equal to the number of values we allocate. This will be 1024 for buffer and 1 for entity.

You should replace those lines with:

buffer.deinitialize(count: 1024)
// and
entity.deinitialize(count: 1)

respectively

However, since this is code from a pod that you are modifying, make sure to check the terms in the licence of the pod to make sure you are not violating anything.

You should also inform the author of the pod that the pod needs updating. This API change is made in Swift 4.1, I think, so it's quite old.

concurrentPerform UnsafeMutablePointer.deinitialize error while adding value to array

The fundamental problem is that you are appending items to an array from multiple threads at the same time. The Swift Array type is not thread-safe. You have to synchronize your interaction with it. For example, you can synchronize it yourself using a serial queue:

DispatchQueue.global().async {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronization")

var array = [4444,5555]

DispatchQueue.concurrentPerform(iterations: 1000) { iter in
var max = 100_000
for iterB in 0 ..< 100_000 {
var k = 0
k = k + 1
var half = max/2
if iterB == half {
queue.async {
array.append(iter)
}
}
}
}

queue.async {
print(array)
}
}

Note, I'd also suggest dispatching the concurrentPerform to a background thread because while it runs code in parallel, it does block the current thread until all of those parallel loops are done.


Or you can use an array type that does the synchronization for you:

DispatchQueue.global().async {
let array = SynchronizedArray(array: [4444,5555])

DispatchQueue.concurrentPerform(iterations: 1000) { iter in
let max = 100_000
for iterB in 0 ..< 100_000 {
var k = 0
k = k + 1
let half = max/2
if iterB == half {
array.append(iter)
}
}
}

print(array)
}

Where

//
// SynchronizedArray.swift
//
// Created by Robert Ryan on 12/24/17.
//

import Foundation

/// A synchronized, ordered, random-access array.
///
/// This provides low-level synchronization for an array, employing the
/// reader-writer pattern (using concurrent queue, allowing concurrent
/// "reads" but using barrier to ensure nothing happens concurrently with
/// respect to "writes".
///
/// - Note: This may not be sufficient to achieve thread-safety,
/// as that is often only achieved with higher-level synchronization.
/// But for many situations, this can be sufficient.

final class SynchronizedArray<Element> {
private var array: [Element]
private let queue: DispatchQueue

init(array: [Element] = [], qos: DispatchQoS = .default) {
self.array = array
self.queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronization", qos: qos, attributes: .concurrent)
}

/// First element in the collection.

var first: Element? {
return queue.sync { self.array.first }
}

/// Last element in the collection.

var last: Element? {
return queue.sync { self.array.last }
}

/// The number of elements in the collection.

var count: Int {
return queue.sync { self.array.count }
}

/// Inserts new element at the specified position.
///
/// - Parameters:
/// - newElement: The element to be inserted.
/// - index: The position at which the element should be inserted.

func insert(_ newElement: Element, at index: Int) {
queue.async(flags: .barrier) {
self.array.insert(newElement, at: index)
}
}

/// Appends new element at the end of the collection.
///
/// - Parameter newElement: The element to be appended.

func append(_ newElement: Element) {
queue.async(flags: .barrier) {
self.array.append(newElement)
}
}

/// Removes all elements from the array.

func removeAll() {
queue.async(flags: .barrier) {
self.array.removeAll()
}
}

/// Remove specific element from the array.
///
/// - Parameter index: The position of the element to be removed.

func remove(at index: Int) {
queue.async(flags: .barrier) {
self.array.remove(at: index)
}
}

/// Retrieve or update the element at a particular position in the array.
///
/// - Parameter index: The position of the element.

subscript(index: Int) -> Element {
get {
return queue.sync { self.array[index] }
}
set {
queue.async(flags: .barrier) { self.array[index] = newValue }
}
}

/// Perform a writer block of code asynchronously within the synchronization system for this array.
///
/// This is used for performing updates, where no result is returned. This is the "writer" in
/// the "reader-writer" pattern, which performs the block asynchronously with a barrier.
///
/// For example, the following checks to see if the array has one or more values, and if so,
/// remove the first item:
///
/// synchronizedArray.writer { array in
/// if array.count > 0 {
/// array.remove(at: 0)
/// }
/// }
///
/// In this example, we use the `writer` method to avoid race conditions between checking
/// the number of items in the array and the removing of the first item.
///
/// If you are not performing updates to the array itself or its values, it is more efficient
/// to use the `reader` method.
///
/// - Parameter block: The block to be performed. This is allowed to mutate the array. This is
/// run on a private synchronization queue using a background thread.

func writer(with block: @escaping (inout [Element]) -> Void) {
queue.async(flags: .barrier) {
block(&self.array)
}
}

/// Perform a "reader" block of code within the synchronization system for this array.
///
/// This is the "reader" in the "reader-writer" pattern. This performs the read synchronously,
/// potentially concurrently with other "readers", but never concurrently with any "writers".
///
/// This is used for reading and performing calculations on the array, where a whole block of
/// code needs to be synchronized. The block may, optionally, return a value.
/// This should not be used for performing updates on the array itself. To do updates,
/// use `writer` method.
///
/// For example, if dealing with array of integers, you could average them with:
///
/// let average = synchronizedArray.reader { array -> Double in
/// let count = array.count
/// let sum = array.reduce(0, +)
/// return Double(sum) / Double(count)
/// }
///
/// This example ensures that there is no race condition between the checking of the
/// number of items in the array and the calculation of the sum of the values.
///
/// - Parameter block: The block to be performed. This is not allowed to mutate the array.
/// This runs on a private synchronization queue (so if you need main queue,
/// you will have to dispatch that yourself).

func reader<U>(block: ([Element]) -> U) -> U {
return queue.sync {
block(self.array)
}
}

/// Retrieve the array for use elsewhere.
///
/// This resulting array is a copy of the underlying `Array` used by `SynchronizedArray`.
/// This copy will not participate in any further synchronization.

var copy: [Element] { return queue.sync { self.array } }
}

extension SynchronizedArray: CustomStringConvertible {
// Use the description of the underlying array

var description: String { return queue.sync { self.array.description } }
}


Related Topics



Leave a reply



Submit