pyobjc icon indicating copy to clipboard operation
pyobjc copied to clipboard

Deallocating objective-C class

Open jonashaag opened this issue 1 year ago • 5 comments

Describe the bug

When running a piece of code, I get this print:

Deallocating objective-C class VNRecognizeTextRequest

It seems like this is unexpected: https://github.com/ronaldoussoren/pyobjc/blob/b4efea53d01d5715de985a4f28758d0c80a64f76/pyobjc-core/Modules/objc/objc-class.m#L1182-L1188

It seems like this only happens if I do a local import in a function:

def foo():
    import Cocoa, Vision, objc

I can try to come up with a reproducer if requested.

Platform information

PyObjC from conda-forge and from pip

❯ pixi list | grep objc
pyobjc-core                               10.3.1           py312hbb55c70_0           479.6 KiB  conda  pyobjc-core-10.3.1-py312hbb55c70_0.conda
pyobjc-framework-cocoa                    10.3.1           py312hbb55c70_0           370.5 KiB  conda  pyobjc-framework-cocoa-10.3.1-py312hbb55c70_0.conda
pyobjc_framework_coreml                   10.3.1                                     111.8 KiB  pypi   pyobjc_framework_coreml-10.3.1-cp36-abi3-macosx_11_0_universal2.http.whl
pyobjc_framework_quartz                   10.3.1                                     1.6 MiB    pypi   pyobjc_framework_quartz-10.3.1-cp312-cp312-macosx_10_9_universal2.http.whl
pyobjc_framework_vision                   10.3.1                                     156.7 KiB  pypi   pyobjc_framework_vision-10.3.1-cp36-abi3-macosx_11_0_universal2.http.whl

macOS version: 14.5 (23F79)

To Reproduce

Not sure how to reproduce in an isolated piece of code (haven't tried).

Expected behavior

Not sure

jonashaag avatar Aug 13 '24 15:08 jonashaag

A reproducer would be nice because this should not happen.

Can you reproduce this in your code when using PyObjC 10.3.0?

ronaldoussoren avatar Aug 14 '24 16:08 ronaldoussoren

Reproducer:

from concurrent.futures import ThreadPoolExecutor

def foobar():
    import Vision
    x = Vision.VNRecognizedTextObservation

with ThreadPoolExecutor() as pool:
    for _ in range(2):
        pool.submit(foobar)

jonashaag avatar Aug 15 '24 05:08 jonashaag

Thanks for the reproducer.

I'm starting to understand what going on here, but do not have a solution at this point. There is a race condition in the ObjC parts of pyobjc-core in updating some core data structures (caused be reentering the interpreter in a block of my code that thinks its protected from reentrancy by the GIL).

BTW. Your timing in reporting this is spot on, I'm working on making PyObjC compatible with the free threaded build of Python 3.13 and this would have been a real head scratcher if I'd found this while testing the free threaded build...

ronaldoussoren avatar Aug 17 '24 11:08 ronaldoussoren

I've committed a fix for this in the master branch, will backport this to the in current stable branch as well.

As a workaround: make sure that classes are imported only on a single thread, such as by importing classes when the module/script is loaded instead of in a nested function.

ronaldoussoren avatar Aug 17 '24 12:08 ronaldoussoren

Thanks for the prompt fix!

jonashaag avatar Aug 17 '24 13:08 jonashaag