Objective-C Interop Extensions
Luna
luna at foxgirls.gay
Sun Nov 17 00:09:06 UTC 2024
On Saturday, 16 November 2024 at 22:47:09 UTC, Nicholas Wilson
wrote:
> On Saturday, 16 November 2024 at 22:29:36 UTC, Luna wrote:
>> ## Proposal
>>
>> Currently DLang has, in some compilers, support for ObjectiveC
>> types being declared. However, modern Objective-C code is
>> strongly dependent on autorelease and auto release pools.
>> Without them Objective-C code leaks memory (see:
>> https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc).
>>
>> To alleviate this, clang implements the concept of an
>> `@autoreleasepool` block, this block frees all Objective-C
>> classes which have called `autorelease`. In the case of Metal,
>> for example, a lot of internal data uses autorelease calls.
>>
>> The `@autoreleasepool` blocks in clang are implemented via 2
>> helper functions declared in the objective-c runtime
>> `libobjc.dylib` or the like in alternate runtime
>> implementations.
>> ```d
>> extern(C) extern void* objc_autoreleasePoolPush();
>> extern(C) extern void objc_autoreleasePoolPop(void*);
>> ```
>>
>> My proposal is to add 2 new hooks to the D runtime emitted by
>> the compiler as well as a new keyword, `autoreleasepool`. This
>> keyword should be followed by a block.
>>
>> The runtime hooks should have a default implementation which
>> do nothing.
>> ```d
>> void* _d_pool_enter() { return null; }
>> void _d_pool_leave(void*) { }
>> ```
>>
>> Whenever an `autoreleasepool` block is encountered by the
>> compiler, the following code should be emitted.
>> ```d
>> // Note: __ctx_ptr is just a placeholder, the name of the
>> variable should be unique.
>> void* __ctx_ptr = _d_pool_enter();
>>
>> // Code goes here
>>
>> _d_pool_leave(__ctx_ptr);
>> ```
>>
>> autorelease pools should additionally, on macOS, automatically
>> be generated in the runtime entry point and wrap the user D
>> main function, and threads created in D should also include
>> pool enter and leave calls.
>>
>> If the D compiler links to a library defining
>> `objc_autoreleasePoolPush` and `objc_autoreleasePoolPop`, the
>> D runtime hooks should link against those, instead.
>>
>> ## Why not use NSAutoreleasePool?
>>
>> The `Foundation` library includes a class called
>> NSAutoreleasePool, however i've found that if Objective-C's
>> runtime is compiled with ARC (Automatic Reference Counting)
>> support, then NSAutoreleasePool becomes no-op in some
>> instances, which in turn causes memory leaks.
>
> This would be easy to do, synchronised works in pretty much the
> exact same way, but why does this need to be a compiler thing?
> i.e. what does it offer that
>
> ```d
> struct AutoreleasePool
> {
> private void* __p;
> static AutoreleasePool opCall()
> {
> AutoreleasePool ret;
> ret.__p = objc_autoreleasePoolPush();
> return ret;
> }
> ~this () { objc_autoreleasePoolPop(__p); }
> }
>
> void main()
> {
> auto x = AutoreleasePool();
> }
> ```
> does not?
>
> I think I saw some of your code that used a scope delegate as
> `autorelease((){ ... });` why is that not sufficient?
>
> Given how niche this is, there had better be a very good reason
> for adding it, e.g. generality with regular D code, not just
> Obj-C.
There's a few subtleties that would make this change in
particular good to have on macOS at least. Given that, as said,
threads and the runtime entry and exits needs to have implicit
autoreleasepools to avoid potentially leaking both memory, but
more importantly: kernel handles. A lot of functionality in macOS
relies on getting shared memory handles that are wrapped in
autorelease handlers; and without a pool to clean them up you can
end up with a bunch of zombie handles exhausting the available
handles in the system.
Having this implicit and explicit control over autorelease
behaviour would be a massive benefit and memory safety
improvement when working on cross-platform software in D, as you
can not avoid relying on Objective-C, eg. if you're making
graphical applications.
More information about the dip.ideas
mailing list