-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
FreeTypeFont is not thread-safe with free threading #9497
Copy link
Copy link
Closed
Labels
Free-threadingPEP 703 supportPEP 703 support
Description
This was originally discovered by a colleague. Using a FreeTypeFont created by ImageFont.truetype(...) from multiple threads crashes.
This looks like a limitation of the freetype library:
I think we can use a critical section to lock around operations on a FontObject / FT_Face.
What actually happened?
Segmentation fault
What are your OS, Python and Pillow versions?
- OS: linux
- Python: 3.14t
- Pillow: 12.2.0.dev0
Pillow version:
Pillow 12.2.0.dev0
Python 3.14.3+ free-threading build (heads/3.14:fa3143a1d2d, Mar 25 2026, 14:10:06) [Clang 20.1.8 (++20250804090239+87f0227cb601-1~exp1~20250804210352.139)]
--------------------------------------------------------------------
Python executable is /home/sgross/cpython/python
System Python files loaded from /usr/local
--------------------------------------------------------------------
Python Pillow modules loaded from /home/sgross/pillow/src/PIL
Binary Pillow modules loaded from /home/sgross/pillow/src/PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 12.2.0.dev0
--- TKINTER support ok, loaded 8.6
--- FREETYPE2 support ok, loaded 2.13.2
*** LITTLECMS2 support not installed
*** WEBP support not installed
*** AVIF support not installed
--- JPEG support ok, compiled for libjpeg-turbo 2.1.5
*** OPENJPEG (JPEG2000) support not installed
--- ZLIB (PNG/ZIP) support ok, loaded 1.3
*** LIBTIFF support not installed
*** RAQM (Bidirectional Text) support not installed
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------
import sys
import threading
from PIL import ImageFont
for i in range(10):
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", size=24)
barrier = threading.Barrier(4)
def worker():
barrier.wait()
for _ in range(1000):
font.getmetrics()
font.getlength("Hello, world!")
threads = [threading.Thread(target=worker) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print("ok")Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Free-threadingPEP 703 supportPEP 703 support