Skip to content

Commit aa0aa04

Browse files
ma8mavstinner
authored andcommitted
bpo-30832: Remove own implementation for thread-local storage (#2537)
* bpo-30832: Remove own implementation for thread-local storage CPython has provided the own implementation for thread-local storage (TLS) on Python/thread.c, it's used in the case which a platform has not supplied native TLS. However, currently all supported platforms (NT and pthreads) have provided native TLS and defined the Py_HAVE_NATIVE_TLS macro with unconditional in any case. * bpo-30832: replace NT with Windows * bpo-30832: change to directive chain * bpo-30832: remove comemnt which making no sense
1 parent 5e87592 commit aa0aa04

File tree

4 files changed

+15
-220
lines changed

4 files changed

+15
-220
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Remove own implementation for thread-local storage.
2+
3+
CPython has provided the own implementation for thread-local storage (TLS)
4+
on Python/thread.c, it's used in the case which a platform has not supplied
5+
native TLS. However, currently all supported platforms (Windows and pthreads)
6+
have provided native TLS and defined the Py_HAVE_NATIVE_TLS macro with
7+
unconditional in any case.

Python/thread.c

Lines changed: 8 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@ PyThread_init_thread(void)
8181
or the size specified by the THREAD_STACK_SIZE macro. */
8282
static size_t _pythread_stacksize = 0;
8383

84-
#ifdef _POSIX_THREADS
85-
#define PYTHREAD_NAME "pthread"
86-
#include "thread_pthread.h"
87-
#endif
88-
89-
#ifdef NT_THREADS
90-
#define PYTHREAD_NAME "nt"
91-
#include "thread_nt.h"
84+
#if defined(_POSIX_THREADS)
85+
# define PYTHREAD_NAME "pthread"
86+
# include "thread_pthread.h"
87+
#elif defined(NT_THREADS)
88+
# define PYTHREAD_NAME "nt"
89+
# include "thread_nt.h"
90+
#else
91+
# error "Require native thread feature. See https://bugs.python.org/issue30832"
9292
#endif
9393

9494

@@ -114,13 +114,7 @@ PyThread_set_stacksize(size_t size)
114114
#endif
115115
}
116116

117-
#ifndef Py_HAVE_NATIVE_TLS
118-
/* If the platform has not supplied a platform specific
119-
TLS implementation, provide our own.
120117

121-
This code stolen from "thread_sgi.h", where it was the only
122-
implementation of an existing Python TLS API.
123-
*/
124118
/* ------------------------------------------------------------------------
125119
Per-thread data ("key") support.
126120
@@ -157,205 +151,6 @@ any of the other functions are called. There's also a hidden assumption
157151
that calls to PyThread_create_key() are serialized externally.
158152
------------------------------------------------------------------------ */
159153

160-
/* A singly-linked list of struct key objects remembers all the key->value
161-
* associations. File static keyhead heads the list. keymutex is used
162-
* to enforce exclusion internally.
163-
*/
164-
struct key {
165-
/* Next record in the list, or NULL if this is the last record. */
166-
struct key *next;
167-
168-
/* The thread id, according to PyThread_get_thread_ident(). */
169-
unsigned long id;
170-
171-
/* The key and its associated value. */
172-
int key;
173-
void *value;
174-
};
175-
176-
static struct key *keyhead = NULL;
177-
static PyThread_type_lock keymutex = NULL;
178-
static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */
179-
180-
/* Internal helper.
181-
* If the current thread has a mapping for key, the appropriate struct key*
182-
* is returned. NB: value is ignored in this case!
183-
* If there is no mapping for key in the current thread, then:
184-
* If value is NULL, NULL is returned.
185-
* Else a mapping of key to value is created for the current thread,
186-
* and a pointer to a new struct key* is returned; except that if
187-
* malloc() can't find room for a new struct key*, NULL is returned.
188-
* So when value==NULL, this acts like a pure lookup routine, and when
189-
* value!=NULL, this acts like dict.setdefault(), returning an existing
190-
* mapping if one exists, else creating a new mapping.
191-
*
192-
* Caution: this used to be too clever, trying to hold keymutex only
193-
* around the "p->next = keyhead; keyhead = p" pair. That allowed
194-
* another thread to mutate the list, via key deletion, concurrent with
195-
* find_key() crawling over the list. Hilarity ensued. For example, when
196-
* the for-loop here does "p = p->next", p could end up pointing at a
197-
* record that PyThread_delete_key_value() was concurrently free()'ing.
198-
* That could lead to anything, from failing to find a key that exists, to
199-
* segfaults. Now we lock the whole routine.
200-
*/
201-
static struct key *
202-
find_key(int set_value, int key, void *value)
203-
{
204-
struct key *p, *prev_p;
205-
unsigned long id = PyThread_get_thread_ident();
206-
207-
if (!keymutex)
208-
return NULL;
209-
PyThread_acquire_lock(keymutex, 1);
210-
prev_p = NULL;
211-
for (p = keyhead; p != NULL; p = p->next) {
212-
if (p->id == id && p->key == key) {
213-
if (set_value)
214-
p->value = value;
215-
goto Done;
216-
}
217-
/* Sanity check. These states should never happen but if
218-
* they do we must abort. Otherwise we'll end up spinning
219-
* in a tight loop with the lock held. A similar check is done
220-
* in pystate.c tstate_delete_common(). */
221-
if (p == prev_p)
222-
Py_FatalError("tls find_key: small circular list(!)");
223-
prev_p = p;
224-
if (p->next == keyhead)
225-
Py_FatalError("tls find_key: circular list(!)");
226-
}
227-
if (!set_value && value == NULL) {
228-
assert(p == NULL);
229-
goto Done;
230-
}
231-
p = (struct key *)PyMem_RawMalloc(sizeof(struct key));
232-
if (p != NULL) {
233-
p->id = id;
234-
p->key = key;
235-
p->value = value;
236-
p->next = keyhead;
237-
keyhead = p;
238-
}
239-
Done:
240-
PyThread_release_lock(keymutex);
241-
return p;
242-
}
243-
244-
/* Return a new key. This must be called before any other functions in
245-
* this family, and callers must arrange to serialize calls to this
246-
* function. No violations are detected.
247-
*/
248-
int
249-
PyThread_create_key(void)
250-
{
251-
/* All parts of this function are wrong if it's called by multiple
252-
* threads simultaneously.
253-
*/
254-
if (keymutex == NULL)
255-
keymutex = PyThread_allocate_lock();
256-
return ++nkeys;
257-
}
258-
259-
/* Forget the associations for key across *all* threads. */
260-
void
261-
PyThread_delete_key(int key)
262-
{
263-
struct key *p, **q;
264-
265-
PyThread_acquire_lock(keymutex, 1);
266-
q = &keyhead;
267-
while ((p = *q) != NULL) {
268-
if (p->key == key) {
269-
*q = p->next;
270-
PyMem_RawFree((void *)p);
271-
/* NB This does *not* free p->value! */
272-
}
273-
else
274-
q = &p->next;
275-
}
276-
PyThread_release_lock(keymutex);
277-
}
278-
279-
int
280-
PyThread_set_key_value(int key, void *value)
281-
{
282-
struct key *p;
283-
284-
p = find_key(1, key, value);
285-
if (p == NULL)
286-
return -1;
287-
else
288-
return 0;
289-
}
290-
291-
/* Retrieve the value associated with key in the current thread, or NULL
292-
* if the current thread doesn't have an association for key.
293-
*/
294-
void *
295-
PyThread_get_key_value(int key)
296-
{
297-
struct key *p = find_key(0, key, NULL);
298-
299-
if (p == NULL)
300-
return NULL;
301-
else
302-
return p->value;
303-
}
304-
305-
/* Forget the current thread's association for key, if any. */
306-
void
307-
PyThread_delete_key_value(int key)
308-
{
309-
unsigned long id = PyThread_get_thread_ident();
310-
struct key *p, **q;
311-
312-
PyThread_acquire_lock(keymutex, 1);
313-
q = &keyhead;
314-
while ((p = *q) != NULL) {
315-
if (p->key == key && p->id == id) {
316-
*q = p->next;
317-
PyMem_RawFree((void *)p);
318-
/* NB This does *not* free p->value! */
319-
break;
320-
}
321-
else
322-
q = &p->next;
323-
}
324-
PyThread_release_lock(keymutex);
325-
}
326-
327-
/* Forget everything not associated with the current thread id.
328-
* This function is called from PyOS_AfterFork_Child(). It is necessary
329-
* because other thread ids which were in use at the time of the fork
330-
* may be reused for new threads created in the forked process.
331-
*/
332-
void
333-
PyThread_ReInitTLS(void)
334-
{
335-
unsigned long id = PyThread_get_thread_ident();
336-
struct key *p, **q;
337-
338-
if (!keymutex)
339-
return;
340-
341-
/* As with interpreter_lock in PyEval_ReInitThreads()
342-
we just create a new lock without freeing the old one */
343-
keymutex = PyThread_allocate_lock();
344-
345-
/* Delete all keys which do not match the current thread id */
346-
q = &keyhead;
347-
while ((p = *q) != NULL) {
348-
if (p->id != id) {
349-
*q = p->next;
350-
PyMem_RawFree((void *)p);
351-
/* NB This does *not* free p->value! */
352-
}
353-
else
354-
q = &p->next;
355-
}
356-
}
357-
358-
#endif /* Py_HAVE_NATIVE_TLS */
359154

360155
PyDoc_STRVAR(threadinfo__doc__,
361156
"sys.thread_info\n\

Python/thread_nt.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,6 @@ _pythread_nt_set_stacksize(size_t size)
348348
#define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)
349349

350350

351-
/* use native Windows TLS functions */
352-
#define Py_HAVE_NATIVE_TLS
353-
354-
#ifdef Py_HAVE_NATIVE_TLS
355351
int
356352
PyThread_create_key(void)
357353
{
@@ -408,5 +404,3 @@ PyThread_delete_key_value(int key)
408404
void
409405
PyThread_ReInitTLS(void)
410406
{}
411-
412-
#endif

Python/thread_pthread.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,6 @@ _pythread_pthread_set_stacksize(size_t size)
608608

609609
#define THREAD_SET_STACKSIZE(x) _pythread_pthread_set_stacksize(x)
610610

611-
#define Py_HAVE_NATIVE_TLS
612611

613612
int
614613
PyThread_create_key(void)

0 commit comments

Comments
 (0)