Skip to content

Commit bb40ffa

Browse files
authored
Merge pull request #233 from lysnikolaou/free-threading
Mark extensions as free-threading-compatible and build wheels
2 parents 7d204aa + 8e9681d commit bb40ffa

File tree

6 files changed

+127
-0
lines changed

6 files changed

+127
-0
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ jobs:
5959
- "3.12"
6060
- "3.13"
6161
- "3.14"
62+
- "3.14t"
6263
- "pypy-3.10"
6364
- "pypy-3.11"
6465
os: ["ubuntu-latest"]

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ version 1.8.0-dev
2121
+ Include test packages in the source distribution, so source distribution
2222
installations can be verified.
2323
+ Fix an issue where some tests failed because they ignored PYTHONPATH.
24+
+ Enable support for free-threading and build free-threaded wheels for
25+
CPython 3.14. Thanks to @lysnikolaou and @ngoldbaum.
2426

2527
version 1.7.2
2628
-----------------

src/isal/_isalmodule.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ PyInit__isal(void)
3131
if (m == NULL) {
3232
return NULL;
3333
}
34+
35+
#ifdef Py_GIL_DISABLED
36+
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
37+
#endif
38+
3439
PyModule_AddIntMacro(m, ISAL_MAJOR_VERSION);
3540
PyModule_AddIntMacro(m, ISAL_MINOR_VERSION);
3641
PyModule_AddIntMacro(m, ISAL_PATCH_VERSION);

src/isal/igzip_libmodule.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,11 @@ PyInit_igzip_lib(void)
617617
if (m == NULL)
618618
return NULL;
619619

620+
#ifdef Py_GIL_DISABLED
621+
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
622+
#endif
623+
624+
620625
IsalError = PyErr_NewException("igzip_lib.IsalError", NULL, NULL);
621626
if (IsalError == NULL) {
622627
return NULL;

src/isal/isal_zlibmodule.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,6 +2183,10 @@ PyInit_isal_zlib(void)
21832183
return NULL;
21842184
}
21852185

2186+
#ifdef Py_GIL_DISABLED
2187+
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
2188+
#endif
2189+
21862190
PyObject *igzip_lib_module = PyImport_ImportModule("isal.igzip_lib");
21872191
if (igzip_lib_module == NULL) {
21882192
return NULL;

tests/test_freethreading.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import concurrent.futures
2+
import random
3+
import string
4+
import threading
5+
6+
from isal import igzip_lib, isal_zlib
7+
8+
import pytest
9+
10+
11+
HAMLET_SCENE = b"""
12+
LAERTES
13+
14+
O, fear me not.
15+
I stay too long: but here my father comes.
16+
17+
Enter POLONIUS
18+
19+
A double blessing is a double grace,
20+
Occasion smiles upon a second leave.
21+
22+
LORD POLONIUS
23+
24+
Yet here, Laertes! aboard, aboard, for shame!
25+
The wind sits in the shoulder of your sail,
26+
And you are stay'd for. There; my blessing with thee!
27+
And these few precepts in thy memory
28+
See thou character. Give thy thoughts no tongue,
29+
Nor any unproportioned thought his act.
30+
Be thou familiar, but by no means vulgar.
31+
Those friends thou hast, and their adoption tried,
32+
Grapple them to thy soul with hoops of steel;
33+
But do not dull thy palm with entertainment
34+
Of each new-hatch'd, unfledged comrade. Beware
35+
Of entrance to a quarrel, but being in,
36+
Bear't that the opposed may beware of thee.
37+
Give every man thy ear, but few thy voice;
38+
Take each man's censure, but reserve thy judgment.
39+
Costly thy habit as thy purse can buy,
40+
But not express'd in fancy; rich, not gaudy;
41+
For the apparel oft proclaims the man,
42+
And they in France of the best rank and station
43+
Are of a most select and generous chief in that.
44+
Neither a borrower nor a lender be;
45+
For loan oft loses both itself and friend,
46+
And borrowing dulls the edge of husbandry.
47+
This above all: to thine ownself be true,
48+
And it must follow, as the night the day,
49+
Thou canst not then be false to any man.
50+
Farewell: my blessing season this in thee!
51+
52+
LAERTES
53+
54+
Most humbly do I take my leave, my lord.
55+
56+
LORD POLONIUS
57+
58+
The time invites you; go; your servants tend.
59+
60+
LAERTES
61+
62+
Farewell, Ophelia; and remember well
63+
What I have said to you.
64+
65+
OPHELIA
66+
67+
'Tis in my memory lock'd,
68+
And you yourself shall keep the key of it.
69+
70+
LAERTES
71+
72+
Farewell.
73+
"""
74+
75+
NUM_THREADS = 10
76+
NUM_ITERATIONS = 20
77+
NUM_JOBS = 50 # To simulate 50 jobs running in 10 threads
78+
barrier = threading.Barrier(parties=NUM_THREADS)
79+
80+
81+
def isal_compress_decompress(compress, decompress):
82+
for _ in range(NUM_ITERATIONS):
83+
barrier.wait()
84+
x = compress(HAMLET_SCENE)
85+
assert decompress(x) == HAMLET_SCENE
86+
87+
length = len(HAMLET_SCENE)
88+
hamlet_random = HAMLET_SCENE + b"".join(
89+
[s.encode() for s in random.choices(string.printable, k=length)]
90+
)
91+
barrier.wait()
92+
x = compress(hamlet_random)
93+
assert decompress(x) == hamlet_random
94+
95+
96+
@pytest.mark.parametrize(
97+
"compress,decompress",
98+
[
99+
pytest.param(isal_zlib.compress, isal_zlib.decompress, id="zlib"),
100+
pytest.param(igzip_lib.compress, igzip_lib.decompress, id="igzip"),
101+
]
102+
)
103+
def test_isal_compress_decompress_threaded(compress, decompress):
104+
with concurrent.futures.ThreadPoolExecutor(NUM_THREADS) as executor:
105+
futures = [
106+
executor.submit(isal_compress_decompress, compress, decompress)
107+
for _ in range(NUM_JOBS)
108+
]
109+
for future in concurrent.futures.as_completed(futures):
110+
future.result() # To fire assertion error if there is one

0 commit comments

Comments
 (0)