@@ -40,9 +40,10 @@ Copyright (C) 1994 Steen Lumholt.
4040#define CHECK_SIZE (size , elemsize ) \
4141 ((size_t)(size) <= Py_MIN((size_t)INT_MAX, UINT_MAX / (size_t)(elemsize)))
4242
43- /* If Tcl is compiled for threads, we must also define TCL_THREAD. We define
44- it always; if Tcl is not threaded, the thread functions in
45- Tcl are empty. */
43+ /* As we require that Tcl is compiled for threads, we must also define
44+ TCL_THREADS. We define it always; if Tcl is not threaded, the thread
45+ functions in Tcl are empty. We check if Tcl is actually compiled for
46+ threads when importing this module. */
4647#define TCL_THREADS
4748
4849#ifdef TK_FRAMEWORK
@@ -172,16 +173,11 @@ _get_tcl_lib_path(void)
172173}
173174#endif /* MS_WINDOWS */
174175
175- /* The threading situation is complicated. Tcl is not thread-safe, except
176- when configured with --enable- threads.
176+ /* The threading situation is complicated.
177+ We require that Tcl is compiled for threads.
177178
178- So we need to use a lock around all uses of Tcl. Previously, the
179- Python interpreter lock was used for this. However, this causes
180- problems when other Python threads need to run while Tcl is blocked
181- waiting for events.
182-
183- To solve this problem, a separate lock for Tcl is introduced.
184- Holding it is incompatible with holding Python's interpreter lock.
179+ We introduce a lock specifically for Tcl; holding it is incompatible
180+ with holding Python's interpreter lock.
185181 The following four macros manipulate both locks together.
186182
187183 ENTER_TCL and LEAVE_TCL are brackets, just like
@@ -213,9 +209,8 @@ _get_tcl_lib_path(void)
213209 These locks expand to several statements and brackets; they should
214210 not be used in branches of if statements and the like.
215211
216- If Tcl is threaded, this approach won't work anymore. The Tcl
217- interpreter is only valid in the thread that created it, and all Tk
218- activity must happen in this thread, also. That means that the
212+ The Tcl interpreter is only valid in the thread that created it, and
213+ all Tk activity must happen in this thread, also. That means that the
219214 mainloop must be invoked in the thread that created the
220215 interpreter. Invoking commands from other threads is possible;
221216 _tkinter will queue an event for the interpreter thread, which will
@@ -225,49 +220,52 @@ _get_tcl_lib_path(void)
225220 the command invocation will block.
226221
227222 In addition, for a threaded Tcl, a single global tcl_tstate won't
228- be sufficient anymore, since multiple Tcl interpreters may
229- simultaneously dispatch in different threads. So we use the Tcl TLS
230- API.
223+ be sufficient, since multiple Tcl interpreters may simultaneously
224+ dispatch in different threads. So we use the Tcl TLS API.
231225
232226*/
233227
234- static PyThread_type_lock tcl_lock = 0 ;
228+ static int
229+ _check_tcl_threaded (void )
230+ {
231+ Tcl_Interp * interp ;
232+ Tcl_Obj * threaded ;
233+ interp = Tcl_CreateInterp ();
234+ threaded = Tcl_GetVar2Ex (interp ,
235+ "tcl_platform" ,
236+ "threaded" ,
237+ TCL_GLOBAL_ONLY );
238+ Tcl_DeleteInterp (interp );
239+ if (threaded == NULL ) return 0 ;
240+ else return 1 ;
241+ }
235242
236- #ifdef TCL_THREADS
237243static Tcl_ThreadDataKey state_key ;
238244typedef PyThreadState * ThreadSpecificData ;
239245#define tcl_tstate \
240246 (*(PyThreadState**)Tcl_GetThreadData(&state_key, sizeof(PyThreadState*)))
241- #else
242- static PyThreadState * tcl_tstate = NULL ;
243- #endif
244247
245248#define ENTER_TCL \
246249 { PyThreadState *tstate = PyThreadState_Get(); \
247250 Py_BEGIN_ALLOW_THREADS \
248- if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); \
249251 tcl_tstate = tstate;
250252
251253#define LEAVE_TCL \
252254 tcl_tstate = NULL; \
253- if(tcl_lock)PyThread_release_lock(tcl_lock); \
254255 Py_END_ALLOW_THREADS}
255256
256257#define ENTER_OVERLAP \
257258 Py_END_ALLOW_THREADS
258259
259260#define LEAVE_OVERLAP_TCL \
260- tcl_tstate = NULL; if(tcl_lock)PyThread_release_lock(tcl_lock); }
261+ tcl_tstate = NULL; }
261262
262263#define ENTER_PYTHON \
263264 { PyThreadState *tstate = tcl_tstate; tcl_tstate = NULL; \
264- if(tcl_lock) \
265- PyThread_release_lock(tcl_lock); \
266265 PyEval_RestoreThread((tstate)); }
267266
268267#define LEAVE_PYTHON \
269268 { PyThreadState *tstate = PyEval_SaveThread(); \
270- if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); \
271269 tcl_tstate = tstate; }
272270
273271#ifndef FREECAST
@@ -282,7 +280,6 @@ typedef struct {
282280 PyObject_HEAD
283281 Tcl_Interp * interp ;
284282 int wantobjects ;
285- int threaded ; /* True if tcl_platform[threaded] */
286283 Tcl_ThreadId thread_id ;
287284 int dispatching ;
288285 PyObject * trace ;
@@ -307,7 +304,7 @@ typedef struct {
307304static inline int
308305check_tcl_appartment (TkappObject * app )
309306{
310- if (app -> threaded && app -> thread_id != Tcl_GetCurrentThread ()) {
307+ if (app -> thread_id != Tcl_GetCurrentThread ()) {
311308 PyErr_SetString (PyExc_RuntimeError ,
312309 "Calling Tcl from different apartment" );
313310 return -1 ;
@@ -575,26 +572,10 @@ Tkapp_New(const char *screenName, const char *className,
575572
576573 v -> interp = Tcl_CreateInterp ();
577574 v -> wantobjects = wantobjects ;
578- v -> threaded = Tcl_GetVar2Ex (v -> interp , "tcl_platform" , "threaded" ,
579- TCL_GLOBAL_ONLY ) != NULL ;
580575 v -> thread_id = Tcl_GetCurrentThread ();
581576 v -> dispatching = 0 ;
582577 v -> trace = NULL ;
583578
584- #ifndef TCL_THREADS
585- if (v -> threaded ) {
586- PyErr_SetString (PyExc_RuntimeError ,
587- "Tcl is threaded but _tkinter is not" );
588- Py_DECREF (v );
589- return 0 ;
590- }
591- #endif
592- if (v -> threaded && tcl_lock ) {
593- /* If Tcl is threaded, we don't need the lock. */
594- PyThread_free_lock (tcl_lock );
595- tcl_lock = NULL ;
596- }
597-
598579 v -> OldBooleanType = Tcl_GetObjType ("boolean" );
599580 {
600581 Tcl_Obj * value ;
@@ -1442,13 +1423,11 @@ Tkapp_CallProc(Tcl_Event *evPtr, int flags)
14421423
14431424
14441425/* This is the main entry point for calling a Tcl command.
1445- It supports three cases, with regard to threading:
1446- 1. Tcl is not threaded: Must have the Tcl lock, then can invoke command in
1447- the context of the calling thread.
1448- 2. Tcl is threaded, caller of the command is in the interpreter thread:
1426+ It supports two cases, with regard to threading:
1427+ 2. Caller of the command is in the interpreter thread:
14491428 Execute the command in the calling thread. Since the Tcl lock will
14501429 not be used, we can merge that with case 1.
1451- 3. Tcl is threaded, caller is in a different thread: Must queue an event to
1430+ 3. Caller is in a different thread: Must queue an event to
14521431 the interpreter thread. Allocation of Tcl objects needs to occur in the
14531432 interpreter thread, so we ship the PyObject* args to the target thread,
14541433 and perform processing there. */
@@ -1469,7 +1448,7 @@ Tkapp_Call(PyObject *selfptr, PyObject *args)
14691448 if (PyTuple_Check (item ))
14701449 args = item ;
14711450 }
1472- if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread ()) {
1451+ if (self -> thread_id != Tcl_GetCurrentThread ()) {
14731452 /* We cannot call the command directly. Instead, we must
14741453 marshal the parameters to the interpreter thread. */
14751454 Tkapp_CallEvent * ev ;
@@ -1746,7 +1725,7 @@ static PyObject*
17461725var_invoke (EventFunc func , PyObject * selfptr , PyObject * args , int flags )
17471726{
17481727 TkappObject * self = TkappObject_CAST (selfptr );
1749- if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread ()) {
1728+ if (self -> thread_id != Tcl_GetCurrentThread ()) {
17501729 VarEvent * ev ;
17511730 // init 'res' and 'exc' to make static analyzers happy
17521731 PyObject * res = NULL , * exc = NULL ;
@@ -1780,7 +1759,7 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags)
17801759 }
17811760 return res ;
17821761 }
1783- /* Tcl is not threaded, or this is the interpreter thread. */
1762+ /* This is the interpreter thread. */
17841763 return func (self , args , flags );
17851764}
17861765
@@ -2438,7 +2417,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
24382417 return NULL ;
24392418 }
24402419
2441- if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread () &&
2420+ if (self -> thread_id != Tcl_GetCurrentThread () &&
24422421 !WaitForMainloop (self ))
24432422 return NULL ;
24442423
@@ -2450,7 +2429,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name,
24502429 Py_INCREF (self );
24512430 data -> self = self ;
24522431 data -> func = Py_NewRef (func );
2453- if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread ()) {
2432+ if (self -> thread_id != Tcl_GetCurrentThread ()) {
24542433 err = 0 ; // init to make static analyzers happy
24552434
24562435 Tcl_Condition cond = NULL ;
@@ -2507,7 +2486,7 @@ _tkinter_tkapp_deletecommand_impl(TkappObject *self, const char *name)
25072486
25082487 TRACE (self , ("((sss))" , "rename" , name , "" ));
25092488
2510- if (self -> threaded && self -> thread_id != Tcl_GetCurrentThread ()) {
2489+ if (self -> thread_id != Tcl_GetCurrentThread ()) {
25112490 err = 0 ; // init to make static analyzers happy
25122491
25132492 Tcl_Condition cond = NULL ;
@@ -2836,8 +2815,6 @@ static PyObject *
28362815_tkinter_tkapp_mainloop_impl (TkappObject * self , int threshold )
28372816/*[clinic end generated code: output=0ba8eabbe57841b0 input=036bcdcf03d5eca0]*/
28382817{
2839- PyThreadState * tstate = PyThreadState_Get ();
2840-
28412818 CHECK_TCL_APPARTMENT (self );
28422819 self -> dispatching = 1 ;
28432820
@@ -2848,23 +2825,10 @@ _tkinter_tkapp_mainloop_impl(TkappObject *self, int threshold)
28482825 {
28492826 int result ;
28502827
2851- if (self -> threaded ) {
2852- /* Allow other Python threads to run. */
2853- ENTER_TCL
2854- result = Tcl_DoOneEvent (0 );
2855- LEAVE_TCL
2856- }
2857- else {
2858- Py_BEGIN_ALLOW_THREADS
2859- if (tcl_lock )PyThread_acquire_lock (tcl_lock , 1 );
2860- tcl_tstate = tstate ;
2861- result = Tcl_DoOneEvent (TCL_DONT_WAIT );
2862- tcl_tstate = NULL ;
2863- if (tcl_lock )PyThread_release_lock (tcl_lock );
2864- if (result == 0 )
2865- Sleep (Tkinter_busywaitinterval );
2866- Py_END_ALLOW_THREADS
2867- }
2828+ /* Allow other Python threads to run. */
2829+ ENTER_TCL
2830+ result = Tcl_DoOneEvent (0 );
2831+ LEAVE_TCL
28682832
28692833 if (PyErr_CheckSignals () != 0 ) {
28702834 self -> dispatching = 0 ;
@@ -3361,13 +3325,11 @@ EventHook(void)
33613325 }
33623326#endif
33633327 Py_BEGIN_ALLOW_THREADS
3364- if (tcl_lock )PyThread_acquire_lock (tcl_lock , 1 );
33653328 tcl_tstate = event_tstate ;
33663329
33673330 result = Tcl_DoOneEvent (TCL_DONT_WAIT );
33683331
33693332 tcl_tstate = NULL ;
3370- if (tcl_lock )PyThread_release_lock (tcl_lock );
33713333 if (result == 0 )
33723334 Sleep (Tkinter_busywaitinterval );
33733335 Py_END_ALLOW_THREADS
@@ -3452,9 +3414,11 @@ PyInit__tkinter(void)
34523414{
34533415 PyObject * m , * uexe , * cexe ;
34543416
3455- tcl_lock = PyThread_allocate_lock ();
3456- if (tcl_lock == NULL )
3457- return NULL ;
3417+ if (_check_tcl_threaded () == 0 ) {
3418+ PyErr_SetString (PyExc_ImportError ,
3419+ "Tcl must be compiled with thread support" );
3420+ return 0 ;
3421+ }
34583422
34593423 m = PyModule_Create (& _tkintermodule );
34603424 if (m == NULL )
0 commit comments