Swift Function as a callback to a C Function

Hi,

Overview:

I have a C function which does processing asynchronously and returns an integer.
I need to use this integer to assign it to ivar in Swift

What I have done

  • I am able to pass the swift function as a callback to the C function

Problem:

  • I am not able to assign the value to an iVar, I get the error A C function pointer cannot be formed from a closure that captures context

My attempt:

  • Used NotificationCenter - It works but just wondering if there is a better way to handle it.

Questions:

  1. What is the preferred way to tackle this?
  2. Would an Objective-C wrapper work?

Code:

BuildImage.h

#ifndef BuildImage_h
#define BuildImage_h

#include <stdio.h>

void doSomething(void (*someFunction) (int));

#endif /* BuildImage_h */

BuildImage.c

#include "BuildImage.h"
#include <unistd.h>

void doSomething(void (*someFunction) (int)) {
    
    sleep(3);
    
    int computedValue = 5;
    
    someFunction(computedValue);
}

main.swift

class Car {
    
    var computedValue : Int32?
    
    func f1(car: Car) {
        
        doSomething { computedValue in
            print("swift received computed value = \(computedValue)")
            self.computedValue = computedValue //Compilation Error: A C function pointer cannot be formed from a closure that captures context
        }
    }
}

If doSomething really looks like that, or rather, if someFunction really looks like that, then you don’t have any means to communicate with outside world (usually via userInfo of some sort). So you’d need to rely on a global state, of which NotificationCenter is one. You can also have a shared global variable (I know, I cringe every time saying that) that you can signal to, and that the rest of the program can listen from.

1 Like

Thanks @Lantua, assuming we could change the signature of any of the functions both C and Swift, would changing them help us in any way? And what changes would you suggest?

Honestly, I don’t know interop that well. I just know enough good’ol C to know that the signature won’t work.

A quick search looks like you can call an objc functions from c (https://stackoverflow.com/questions/19664403/calling-objective-c-from-c), you’d still need to pass in the object like the SO answer shows. I think you can mark Swift function with @objc essentially turning into objc function and try to call it. Though it’d be better to have someone more knowledgable passing by.

1 Like

One pattern that's common in C APIs like this is to take a void *context argument that is passed through to the callback function. You can then use Unmanaged and friends to pass your Car instance through that context into your completion handler. It's a bit fiddly and you'll need to be mindful of memory management, but it'd do the job.

3 Likes

Thanks @ahti and @Lantua

Still new to calling C APIs from Swift, I like the suggestion of passing the context to the callback function. I will try that, thanks a lot!!

If you allow the self-promotion, I wrote an article how to do this a few years ago: C Callbacks in Swift. It uses Unmanaged to pass a Swift object to a C callback via a void *context parameter.

2 Likes

Thanks a lot @ole, thanks for your example in your website.

Based on the suggestions I have given below my implementation.

Feel free to correct if I am missing something.

It works as expected, however I have some questions.

Questions:

  • How to create an instance of UnsafeRawPointer from an instance of Car?
  • I just got away by using inout variable, but assume it was let c1 = Car(), how do I create an UnsafeRawPointer?

Code:

doSomething.h

#ifndef doSomething_h
#define doSomething_h

#include <stdio.h>

void doSomething(const void* someInstance, void (*callbackFunction) (const void*, int));

#endif /* doSomething_h */

doSomething.c

#include "doSomething.h"
#include <unistd.h>

void doSomething(const void* someInstance, void (*callbackFunction) (const void*, int)) {
    
    sleep(3);
    int computedValue = 5;
    callbackFunction(someInstance, computedValue);
}

main.swift

class Car{
    var computedValue : Int32?
}

var c1 = Car()

doSomething(&c1) { someInstance, computedValue in
    
    guard let c2 = someInstance?.load(as: Car.self) else {
        return
    }
    
    c2.computedValue = computedValue
    
    print("c2.computedValue = \(String(describing: c2.computedValue))")
}

Output:

c2.computedValue = Optional(5)
Program ended with exit code: 0

Using the & operator to construct a pointer is potentially dangerous because the lifetime of the pointer is tied to the lifetime of the doSomething function. Your code would break if your callback function were executed asynchronously after doSomething returns. In that case, this line would dereference an invalid pointer:

    guard let c2 = someInstance?.load(as: Car.self)

Additionally, if there's a chance that the object referenced by c1 gets deallocated before the callback finishes, your callback would be loading an invalid object because the callback isn't keeping it alive.

Rather than trying to construct a direct pointer to the object, you should wrap it in an Unmanaged instance, as suggested above by @ahti and as I did in my article.

Unmanaged provides two important things for you:

  1. The ability to perform manual reference counting (e.g. passRetained and takeRetainedValue), which ensures that your object won't be deallocated before the callback finishes. Since C doesn't participate in Swift's Automatic Reference Counting, you have to do this manually (unless you can be sure that the object's lifetime will be longer than the callback's, but even then it's a good idea).

  2. The ability to convert your Unmanaged instance to and from a raw pointer, using toOpaque and fromOpaque.

2 Likes

Thanks a lot @ole, I was making a lot of wrong assumptions.

Yes my code is asynchronous and it is possible the async processing could out live c1.

Given below is a revision of the code:

main.swift

class Car{
    var computedValue : Int32?
}

let c1 = Car()

//This will increase retain count by 1
let p1 = Unmanaged.passRetained(c1).toOpaque()

doSomething(p1) { someInstance, computedValue in
     
    guard let someInstance = someInstance else {
        return
    }
    
    let p2 = Unmanaged<Car>.fromOpaque(someInstance)
    
    let c2 = p2.takeUnretainedValue()
    
    c2.computedValue = computedValue
    
    print("c2.computedValue = \(String(describing: c2.computedValue))")
    
    //This will reduce retain count by 1
    p2.release()
}

Question

  • Assume we we had a requirement where we only cared only if the original car instance was alive, if it had been deallocated we didn't need to assign it the new computed value. How could we implement that?
  • Based on my understanding if we didn't retain, then we could end up with a dangling pointer

Your code looks good. Two small things:

    let c2 = p2.takeUnretainedValue()
    ...
    //This will reduce retain count by 1
    p2.release()

Instead of this pattern, you can also use takeRetainedValue() and omit the release call at the end. The local c2 variable strongly references the object for c2's lifetime.

Another small observation:

    guard let someInstance = someInstance else {
        return
    }

Since you control the C code, you could use _Nonnull in the C declaration to make the pointer non-optional in Swift. This only applies if you want the context parameter to be non-optional, of course, i.e. if the function makes no sense without a context param.

Correct. You need a weak reference. Unmanaged doesn't support this out of the box, but you can define a little helper class that stores a weak reference:

final class Weak<T: AnyObject> {
  weak var value: T?

  init(_ value: T) {
    self.value = value
  }
}

Then, when creating the Unmanaged instance, wrap your context object in Weak and pass the Weak instance to Unmanaged. In the callback, unbox the Weak instance as before and then check if the wrapped object still exists:

let c1 = Car()
let weakC1 = Weak(c1)
// This will retain weakC1, but won't retain c1
let p1 = Unmanaged.passRetained(weakC1).toOpaque()

doSomething(p1) { someInstance, computedValue in
    guard let someInstance = someInstance else {
        return
    }
    
    let p2 = Unmanaged<Car>.fromOpaque(someInstance)
    let weakC2 = p2.takeRetainedValue()
    // Unwrap the weak reference if the object still exists
    guard let c2 = weakC2.value else {
        // c1 has been deallocated
        return
    }
    ...
}
2 Likes

@Ole thanks a lot for patiently explaining!! I understand a lot better now.

Doubt:

  • When using passRetained and takeRetainedValue they will both increase the increase the retain count by 1. Would they reduce the retain count by 1 when the corresponding variables (p1 and weakC1) go out of scope?
  • Reason for asking is that the Weak instance retained twice, I do understand that is only a wrapper to the weak instance, however just wanted to check if that wrapper would get released or would there be a leak without an explicit release.

No. takeRetainedValue decrements the reference count. That's what the documentation means by "consumes an unbalanced retain".

API Refcount
passRetained +1
takeRetainedValue −1
passUnretained +0
takeUnretainedValue −0

Check out the source for takeRetainedValue. It does exactly what you did manually:

public struct Unmanaged<Instance: AnyObject> {
  internal unowned(unsafe) var _value: Instance
  …
  public func takeRetainedValue() -> Instance {
    let result = _value
    release()
    return result
  }
  …
}
2 Likes

@ole Thank you so much!!

1 Like

This is actually incorrect. takeUnretainedValue, increments the ref count.

EDIT: Let me elaborate a bit more. This is the relevant snippet of Unmanaged:

@frozen
public struct Unmanaged<Instance: AnyObject> {
  @usableFromInline
  internal unowned(unsafe) var _value: Instance
  ...
  public func takeUnretainedValue() -> Instance {
    return _value
  }
}

What is actually happening at the implementation level is that the compiler views _value as a trivial (that is non-reference counted value) that one must convert to a strong reference via a special conversion instruction that the optimizer can not eliminate. So when one just returns _value here, one is actually getting that +1 from the conversion from an unmanaged trivial thing to a strong non-trivial thing.

Your confusion here is one reason why I find the name for these APIs extremely confusing. Speaking for myself, I hope at some point in the future we can rectify this (perhaps if we get an opportunity to relax the AnyObject restriction here and thus need to create a new type).

2 Likes

Ole's description is correct for the traditional +0 / +1 terminology from ObjC manual reference-counting, which centers responsibilities and conventions. If you produce an Unmanaged with passRetained and consume it with takeRetainedValue, you are doing a "+1" "retained" transfer of the value, which should always work. If you produce an Unmanaged with passUnretained and consume it with takeUnretainedValue, you are doing a "+0" "unretained" transfer of the value, which relies on something else keeping the value alive between those points.

The retained/unretained terminology is confusing if you look at it from an operational "count the retains and releases" perspective, but that has never been a good way to write maintainable reference-counting code.

5 Likes

@John_McCall Ok. I see what you are saying. That being said, many people get confused and assume that this should not have a retain. So I wanted to be clear that there will be a retain there so care must be used.

3 Likes

@Michael_Gottesman @John_McCall, thanks a lot for your explanations.

I am still learning this so I am bit confused, so I have some questions / clarifications.

Consider the following example:

Code:

class Car{
    var computedValue : Int32?
}

let c1 = Car()

let p1 = Unmanaged.passUnretained(c1).toOpaque()

doSomething(p1) { someInstance, computedValue in
     
    let p2 = Unmanaged<Car>.fromOpaque(someInstance).takeUnretainedValue()
    
    p2.computedValue = computedValue
    
    print("p2.computedValue = \(String(describing: p2.computedValue))")
}

When the _value is returned by takeUnretainedValue the compiler does +1.

Questions / Clarifications:

  1. Does p1 (because of _value returned by takeUnretainedValue) cause the compiler to do +1 implicitly and when p1 out of scope would the compiler do a -1?
  2. Is the above example still unsafe in spite of the compiler's implicit +1 because doSomething is asynchronous and it's completion handler could be executed after p1's lifetime?
  3. Are the above 2 questions the reason why it is not a good way to look at from the "operational (count the retains and releases) perspective"?

Yes, that is exactly why it is not a good idea to think about it as a sequence of retain/release operations rather than thinking about the ownership of a particular value.

passRetainedValue creates an owned reference that it is your obligation to consume with takeRetainedValue exactly once.

3 Likes

To answer your questions directly:

  1. No. Creating an unmanaged reference with passUnretainedValue does not directly increase the reference count, and the compiler will not balance anything out for you automatically.

  2. The above is unsafe because nothing is guaranteeing to keep c1 alive while you have your Unmanaged reference. There is no “implicit +1”. If you instead used passRetainedValue and takeRetainedValue, this would work, assuming doSomething calls its parameter function exactly once.

2 Likes