Skip to content

Commit ee22931

Browse files
committed
make global flags free-threaded friendly
1 parent bd393ae commit ee22931

File tree

1 file changed

+146
-33
lines changed

1 file changed

+146
-33
lines changed

Modules/_cursesmodule.c

+146-33
Original file line numberDiff line numberDiff line change
@@ -192,17 +192,118 @@ class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type"
192192
[clinic start generated code]*/
193193
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/
194194

195-
/* Tells whether setupterm() has been called to initialise terminfo. */
195+
/*
196+
* Tells whether setupterm() has been called to initialise terminfo.
197+
*
198+
* Use curses_setupterm_{was,set}_called() for atomic reads and writes.
199+
*/
196200
static int curses_setupterm_called = FALSE;
197201

198-
/* Tells whether initscr() has been called to initialise curses. */
202+
static inline int
203+
curses_setupterm_was_called(void)
204+
{
205+
#ifdef Py_GIL_DISABLED
206+
return _Py_atomic_load_int(&curses_setupterm_called);
207+
#else
208+
return curses_setupterm_called;
209+
#endif
210+
}
211+
212+
static inline void
213+
curses_setupterm_set_called(void)
214+
{
215+
#ifdef Py_GIL_DISABLED
216+
_Py_atomic_store_int(&curses_setupterm_called, TRUE);
217+
#else
218+
curses_setupterm_called = called ? TRUE : FALSE;
219+
#endif
220+
}
221+
222+
/*
223+
* Tells whether initscr() has been called to initialise curses.
224+
*
225+
* Use curses_initscr_{was,set}_called() for atomic reads and writes.
226+
*/
199227
static int curses_initscr_called = FALSE;
200228

201-
/* Tells whether start_color() has been called to initialise color usage. */
229+
static inline int
230+
curses_initscr_was_called(void)
231+
{
232+
#ifdef Py_GIL_DISABLED
233+
return _Py_atomic_load_int(&curses_initscr_called);
234+
#else
235+
return curses_initscr_called;
236+
#endif
237+
}
238+
239+
static inline void
240+
curses_initscr_set_called(void)
241+
{
242+
#ifdef Py_GIL_DISABLED
243+
_Py_atomic_store_int(&curses_initscr_called, TRUE);
244+
#else
245+
curses_initscr_called = TRUE;
246+
#endif
247+
}
248+
249+
/*
250+
* Tells whether start_color() has been called to initialise color usage.
251+
*
252+
* Use curses_start_color_{was,set}_called() for atomic reads and writes.
253+
*/
202254
static int curses_start_color_called = FALSE;
203255

256+
static inline int
257+
curses_start_color_was_called(void)
258+
{
259+
#ifdef Py_GIL_DISABLED
260+
return _Py_atomic_load_int(&curses_start_color_called);
261+
#else
262+
return curses_start_color_called;
263+
#endif
264+
}
265+
266+
static inline void
267+
curses_start_color_set_called(void)
268+
{
269+
#ifdef Py_GIL_DISABLED
270+
_Py_atomic_store_int(&curses_start_color_called, TRUE);
271+
#else
272+
curses_start_color_called = TRUE;
273+
#endif
274+
}
275+
276+
/*
277+
* The curses screen encoding.
278+
*
279+
* Use curses_{get,set}_screen_encoding() to
280+
* safely (atomically) access this variable.
281+
*/
204282
static const char *curses_screen_encoding = NULL;
205283

284+
/* Atomically retrieve the current screen encoding. */
285+
static inline const char *
286+
curses_get_screen_encoding(void)
287+
{
288+
#ifdef Py_GIL_DISABLED
289+
return (const char *)_Py_atomic_load_ptr(&curses_screen_encoding);
290+
#else
291+
return curses_screen_encoding;
292+
#endif
293+
}
294+
295+
/* Atomically set the current screen encoding. */
296+
static inline void
297+
curses_set_screen_encoding(const char *encoding)
298+
{
299+
assert(encoding != NULL);
300+
#ifdef Py_GIL_DISABLED
301+
_Py_atomic_store_ptr(&curses_screen_encoding, (char *)encoding);
302+
#else
303+
curses_screen_encoding = encoding;
304+
#endif
305+
}
306+
206307
/* Utility Checking Procedures */
207308

208309
/*
@@ -215,7 +316,7 @@ static const char *curses_screen_encoding = NULL;
215316
* type is directly taken from the global state for now.
216317
*/
217318
static inline int
218-
_PyCursesCheckFunction(int called, const char *funcname)
319+
_PyCursesStatelessCheckWasCalled(int called, const char *funcname)
219320
{
220321
if (called == TRUE) {
221322
return 1;
@@ -232,7 +333,7 @@ _PyCursesCheckFunction(int called, const char *funcname)
232333
* The exception type is obtained from the 'module' state.
233334
*/
234335
static inline int
235-
_PyCursesStatefulCheckFunction(PyObject *module, int called, const char *funcname)
336+
_PyCursesCheckWasCalled(PyObject *module, int called, const char *funcname)
236337
{
237338
if (called == TRUE) {
238339
return 1;
@@ -244,30 +345,24 @@ _PyCursesStatefulCheckFunction(PyObject *module, int called, const char *funcnam
244345

245346
#define PyCursesStatefulSetupTermCalled(MODULE) \
246347
do { \
247-
if (!_PyCursesStatefulCheckFunction(MODULE, \
248-
curses_setupterm_called, \
249-
"setupterm")) \
250-
{ \
348+
int called = curses_setupterm_was_called(); \
349+
if (!_PyCursesCheckWasCalled(MODULE, called, "setupterm")) { \
251350
return 0; \
252351
} \
253352
} while (0)
254353

255354
#define PyCursesStatefulInitialised(MODULE) \
256355
do { \
257-
if (!_PyCursesStatefulCheckFunction(MODULE, \
258-
curses_initscr_called, \
259-
"initscr")) \
260-
{ \
356+
int called = curses_initscr_was_called(); \
357+
if (!_PyCursesCheckWasCalled(MODULE, called, "initscr")) { \
261358
return 0; \
262359
} \
263360
} while (0)
264361

265362
#define PyCursesStatefulInitialisedColor(MODULE) \
266363
do { \
267-
if (!_PyCursesStatefulCheckFunction(MODULE, \
268-
curses_start_color_called, \
269-
"start_color")) \
270-
{ \
364+
int called = curses_start_color_was_called(); \
365+
if (!_PyCursesCheckWasCalled(MODULE, called, "start_color")) { \
271366
return 0; \
272367
} \
273368
} while (0)
@@ -350,7 +445,7 @@ PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch)
350445
if (win)
351446
encoding = win->encoding;
352447
else
353-
encoding = curses_screen_encoding;
448+
encoding = curses_get_screen_encoding();
354449
bytes = PyUnicode_AsEncodedString(obj, encoding, NULL);
355450
if (bytes == NULL)
356451
return 0;
@@ -3356,7 +3451,7 @@ _curses_initscr_impl(PyObject *module)
33563451
{
33573452
WINDOW *win;
33583453

3359-
if (curses_initscr_called) {
3454+
if (curses_initscr_was_called()) {
33603455
wrefresh(stdscr);
33613456
_cursesmodule_state *state = get_cursesmodule_state(module);
33623457
return PyCursesWindow_New(state, stdscr, NULL);
@@ -3370,7 +3465,8 @@ _curses_initscr_impl(PyObject *module)
33703465
return NULL;
33713466
}
33723467

3373-
curses_initscr_called = curses_setupterm_called = TRUE;
3468+
curses_initscr_set_called();
3469+
curses_setupterm_set_called();
33743470

33753471
PyObject *module_dict = PyModule_GetDict(module); // borrowed
33763472
if (module_dict == NULL) {
@@ -3467,7 +3563,7 @@ _curses_initscr_impl(PyObject *module)
34673563
if (winobj == NULL) {
34683564
return NULL;
34693565
}
3470-
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
3566+
curses_set_screen_encoding(((PyCursesWindowObject *)winobj)->encoding);
34713567
return winobj;
34723568
}
34733569

@@ -3522,8 +3618,7 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd)
35223618
return NULL;
35233619
}
35243620

3525-
curses_setupterm_called = TRUE;
3526-
3621+
curses_setupterm_set_called();
35273622
Py_RETURN_NONE;
35283623
}
35293624

@@ -4332,7 +4427,7 @@ _curses_start_color_impl(PyObject *module)
43324427
return NULL;
43334428
}
43344429

4335-
curses_start_color_called = TRUE;
4430+
curses_start_color_set_called();
43364431

43374432
PyObject *module_dict = PyModule_GetDict(module); // borrowed
43384433
if (module_dict == NULL) {
@@ -4854,21 +4949,24 @@ static PyMethodDef PyCurses_methods[] = {
48544949
initialised or not. */
48554950

48564951
static inline int
4857-
curses_capi_setupterm_called(void)
4952+
curses_capi_setupterm_was_called(void)
48584953
{
4859-
return _PyCursesCheckFunction(curses_setupterm_called, "setupterm");
4954+
int called = curses_setupterm_was_called();
4955+
return _PyCursesStatelessCheckWasCalled(called, "setupterm");
48604956
}
48614957

48624958
static inline int
4863-
curses_capi_initscr_called(void)
4959+
curses_capi_initscr_was_called(void)
48644960
{
4865-
return _PyCursesCheckFunction(curses_initscr_called, "initscr");
4961+
int called = curses_initscr_was_called();
4962+
return _PyCursesStatelessCheckWasCalled(called, "initscr");
48664963
}
48674964

48684965
static inline int
4869-
curses_capi_start_color_called(void)
4966+
curses_capi_start_color_was_called(void)
48704967
{
4871-
return _PyCursesCheckFunction(curses_start_color_called, "start_color");
4968+
int called = curses_start_color_was_called();
4969+
return _PyCursesStatelessCheckWasCalled(called, "start_color");
48724970
}
48734971

48744972
static void *
@@ -4881,9 +4979,9 @@ curses_capi_new(_cursesmodule_state *state)
48814979
return NULL;
48824980
}
48834981
capi[0] = (void *)Py_NewRef(state->window_type);
4884-
capi[1] = curses_capi_setupterm_called;
4885-
capi[2] = curses_capi_initscr_called;
4886-
capi[3] = curses_capi_start_color_called;
4982+
capi[1] = curses_capi_setupterm_was_called;
4983+
capi[2] = curses_capi_initscr_was_called;
4984+
capi[3] = curses_capi_start_color_was_called;
48874985
return (void *)capi;
48884986
}
48894987

@@ -4944,9 +5042,24 @@ curses_capi_capsule_new(void *capi)
49445042

49455043
/* Module initialization */
49465044

5045+
/* Indicate whether the module has been loaded or not. */
5046+
static int curses_module_loaded = 0;
5047+
49475048
static int
49485049
cursesmodule_exec(PyObject *module)
49495050
{
5051+
if (_Py_atomic_load_int(&curses_module_loaded)) {
5052+
PyErr_SetString(PyExc_ImportError,
5053+
"module 'curses' can only be loaded once per process");
5054+
return -1;
5055+
}
5056+
5057+
#ifdef Py_GIL_DISABLED
5058+
_Py_atomic_store_int(&curses_module_loaded, 1);
5059+
#else
5060+
curses_module_loaded = 1;
5061+
#endif
5062+
49505063
_cursesmodule_state *state = get_cursesmodule_state(module);
49515064
/* Initialize object type */
49525065
state->window_type = (PyTypeObject *)PyType_FromModuleAndSpec(

0 commit comments

Comments
 (0)