emcconville / wand

The ctypes-based simple ImageMagick binding for Python
http://docs.wand-py.org/
Other
1.4k stars 197 forks source link

MacOS seg fault when using Wand in a ThreadPoolExecutor #662

Open boboli opened 6 hours ago

boboli commented 6 hours ago

I wrote a script that uses Wand/ImageMagick to assign an ICC profile to images. I parallelized it by using a ThreadPoolExecutor from Python's concurrent.futures module:

with ThreadPoolExecutor(max_workers=10) as ex:
    ex.map(iccme.apply_icc_to_image, (Path(x) for x in args.images))

The relevant Wand usage is:

def apply_icc_to_image(self, image_path : Path) -> None:
    print(f"  Processing {image_path}...")
    with Image(filename=image_path) as image:
        image.profiles["icc"] = self.icc_data
        image.save(filename=image_path)

This script works perfectly on Ubuntu 24.04 and 22.04, yet when I try to run it on MacOS, it always seg faults. Here's the top of the error report:

Thread 1 Crashed:
0   libomp.dylib                           0x103674588 __kmp_release_queuing_lock + 208
1   libomp.dylib                           0x103677acc __kmp_release_queuing_lock(kmp_user_lock*, int) + 12
2   libomp.dylib                           0x10363e740 __kmpc_unset_lock + 144
3   libMagickCore-7.Q16HDRI.10.dylib           0x10330d310 MagickCoreGenesis + 760
4   libffi.dylib                           0x1a7d84050 ffi_call_SYSV + 80
5   libffi.dylib                           0x1a7d8cae0 ffi_call_int + 1212
6   _ctypes.cpython-312-darwin.so          0x1024779e8 _ctypes_callproc + 808
7   _ctypes.cpython-312-darwin.so          0x102470a14 PyCFuncPtr_call + 216
8   Python                                 0x102b2f7b0 _PyObject_MakeTpCall + 128
9   Python                                 0x102c24f68 _PyEval_EvalFrameDefault + 44052
10  Python                                 0x102b49660 gen_send_ex2 + 204
11  Python                                 0x102b484e0 gen_iternext + 36
12  Python                                 0x102c175bc builtin_next + 76
13  Python                                 0x102b81188 cfunction_vectorcall_FASTCALL + 96
14  Python                                 0x102c24e08 _PyEval_EvalFrameDefault + 43700
15  Python                                 0x102b32a0c method_vectorcall + 372
16  Python                                 0x102c28884 _PyObject_CallNoArgs + 80
17  Python                                 0x102c247cc _PyEval_EvalFrameDefault + 42104
18  Python                                 0x102b2f644 _PyObject_FastCallDictTstate + 208
19  Python                                 0x102ba683c slot_tp_init + 212
20  Python                                 0x102b9da74 type_call + 148
21  Python                                 0x102b2f7b0 _PyObject_MakeTpCall + 128
22  Python                                 0x102c24f68 _PyEval_EvalFrameDefault + 44052
23  Python                                 0x102b32950 method_vectorcall + 184
24  Python                                 0x102c268c0 _PyEval_EvalFrameDefault + 50540
25  Python                                 0x102b32a0c method_vectorcall + 372
26  Python                                 0x102cf6768 thread_run + 144
27  Python                                 0x102c8cc5c pythread_wrapper + 48
28  libsystem_pthread.dylib                0x196db5f94 _pthread_start + 136
29  libsystem_pthread.dylib                0x196db0d34 thread_start + 8

Here's the full report: error.txt

I can work around this in the meantime by switching to a ProcessPoolExecutor which works flawlessly on MacOS as well, but I would like to go back to using threads since the task is mostly I/O-bound and not CPU-bound.

boboli commented 6 hours ago

Ah sorry, forgot to mention my env: M2 Mac Mini on MacOS 14.6.1 Python 3.12.6 Wand 0.6.13 ImageMagick 7.1.1-38 Q16-HDRI aarch64 22398 from Homebrew