Skip to content

Commit

Permalink
bpo-6532: Make the thread id an unsigned integer. (python#781)
Browse files Browse the repository at this point in the history
* bpo-6532: Make the thread id an unsigned integer.

From C API side the type of results of PyThread_start_new_thread() and
PyThread_get_thread_ident(), the id parameter of
PyThreadState_SetAsyncExc(), and the thread_id field of PyThreadState
changed from "long" to "unsigned long".

* Restore a check in thread_get_ident().
  • Loading branch information
serhiy-storchaka authored and vstinner committed Mar 23, 2017
1 parent 1e2147b commit aefa7eb
Show file tree
Hide file tree
Showing 27 changed files with 116 additions and 88 deletions.
5 changes: 4 additions & 1 deletion Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ been created.
the caller should assume no current thread state is available.
.. c:function:: int PyThreadState_SetAsyncExc(long id, PyObject *exc)
.. c:function:: int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)
Asynchronously raise an exception in a thread. The *id* argument is the thread
id of the target thread; *exc* is the exception object to be raised. This
Expand All @@ -840,6 +840,9 @@ been created.
zero if the thread id isn't found. If *exc* is :const:`NULL`, the pending
exception (if any) for the thread is cleared. This raises no exceptions.
.. versionchanged:: 3.7
The type of the *id* parameter changed from :c:type:`long` to
:c:type:`unsigned long`.
.. c:function:: void PyEval_AcquireThread(PyThreadState *tstate)
Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ Deprecated
(Contributed by Serhiy Storchaka in :issue:`28692`.)


Changes in the C API
--------------------

- The type of results of :c:func:`PyThread_start_new_thread` and
:c:func:`PyThread_get_thread_ident`, and the *id* parameter of
:c:func:`PyThreadState_SetAsyncExc` changed from :c:type:`long` to
:c:type:`unsigned long`.
(Contributed by Serhiy Storchaka in :issue:`6532`.)


Removed
=======

Expand Down
4 changes: 2 additions & 2 deletions Include/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ typedef struct _ts {
int gilstate_counter;

PyObject *async_exc; /* Asynchronous exception to raise */
long thread_id; /* Thread id where this tstate was created */
unsigned long thread_id; /* Thread id where this tstate was created */

int trash_delete_nesting;
PyObject *trash_delete_later;
Expand Down Expand Up @@ -200,7 +200,7 @@ PyAPI_FUNC(PyThreadState *) _PyThreadState_UncheckedGet(void);

PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *);
PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void);
PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *);
PyAPI_FUNC(int) PyThreadState_SetAsyncExc(unsigned long, PyObject *);


/* Variable and macro for in-line access to current thread state */
Expand Down
8 changes: 6 additions & 2 deletions Include/pythread.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ typedef enum PyLockStatus {
PY_LOCK_INTR
} PyLockStatus;

#ifndef Py_LIMITED_API
#define PYTHREAD_INVALID_THREAD_ID ((unsigned long)-1)
#endif

PyAPI_FUNC(void) PyThread_init_thread(void);
PyAPI_FUNC(long) PyThread_start_new_thread(void (*)(void *), void *);
PyAPI_FUNC(unsigned long) PyThread_start_new_thread(void (*)(void *), void *);
PyAPI_FUNC(void) PyThread_exit_thread(void);
PyAPI_FUNC(long) PyThread_get_thread_ident(void);
PyAPI_FUNC(unsigned long) PyThread_get_thread_ident(void);

PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void);
PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock);
Expand Down
2 changes: 1 addition & 1 deletion Lib/_dummy_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def get_ident():
available, it is safe to assume that the current process is the
only thread. Thus a constant can be safely returned.
"""
return -1
return 1

def allocate_lock():
"""Dummy implementation of _thread.allocate_lock()."""
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_dummy_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ def test_exit(self):
def test_ident(self):
self.assertIsInstance(_thread.get_ident(), int,
"_thread.get_ident() returned a non-integer")
self.assertNotEqual(_thread.get_ident(), 0,
"_thread.get_ident() returned 0")
self.assertGreater(_thread.get_ident(), 0)

def test_LockType(self):
self.assertIsInstance(_thread.allocate_lock(), _thread.LockType,
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ def g456():
thread_id = thread_info[0]

d = sys._current_frames()
for tid in d:
self.assertIsInstance(tid, int)
self.assertGreater(tid, 0)

main_id = threading.get_ident()
self.assertIn(main_id, d)
Expand Down
9 changes: 6 additions & 3 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def test_PyThreadState_SetAsyncExc(self):
ctypes = import_module("ctypes")

set_async_exc = ctypes.pythonapi.PyThreadState_SetAsyncExc
set_async_exc.argtypes = (ctypes.c_ulong, ctypes.py_object)

class AsyncExc(Exception):
pass
Expand All @@ -189,9 +190,11 @@ class AsyncExc(Exception):

# First check it works when setting the exception from the same thread.
tid = threading.get_ident()
self.assertIsInstance(tid, int)
self.assertGreater(tid, 0)

try:
result = set_async_exc(ctypes.c_long(tid), exception)
result = set_async_exc(tid, exception)
# The exception is async, so we might have to keep the VM busy until
# it notices.
while True:
Expand Down Expand Up @@ -237,7 +240,7 @@ def run(self):
# Try a thread id that doesn't make sense.
if verbose:
print(" trying nonsensical thread id")
result = set_async_exc(ctypes.c_long(-1), exception)
result = set_async_exc(-1, exception)
self.assertEqual(result, 0) # no thread states modified

# Now raise an exception in the worker thread.
Expand All @@ -250,7 +253,7 @@ def run(self):
self.assertFalse(t.finished)
if verbose:
print(" attempting to raise asynch exception in worker")
result = set_async_exc(ctypes.c_long(t.id), exception)
result = set_async_exc(t.id, exception)
self.assertEqual(result, 1) # one thread state modified
if verbose:
print(" waiting for worker to say it caught the exception")
Expand Down
4 changes: 2 additions & 2 deletions Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,14 +993,14 @@ def _delete(self):
#
# Must take care to not raise an exception if _dummy_thread is being
# used (and thus this module is being used as an instance of
# dummy_threading). _dummy_thread.get_ident() always returns -1 since
# dummy_threading). _dummy_thread.get_ident() always returns 1 since
# there is only one thread if _dummy_thread is being used. Thus
# len(_active) is always <= 1 here, and any Thread instance created
# overwrites the (if any) thread currently registered in _active.
#
# An instance of _MainThread is always created by 'threading'. This
# gets overwritten the instant an instance of Thread is created; both
# threads return -1 from _dummy_thread.get_ident() and thus have the
# threads return 1 from _dummy_thread.get_ident() and thus have the
# same key in the dict. So when the _MainThread instance created by
# 'threading' tries to clean itself up when atexit calls this method
# it gets a KeyError if another Thread instance was created.
Expand Down
4 changes: 4 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,10 @@ Windows
C API
-----

- bpo-6532: The type of results of PyThread_start_new_thread() and
PyThread_get_thread_ident(), and the id parameter of
PyThreadState_SetAsyncExc() changed from "long" to "unsigned long".

- Issue #27867: Function PySlice_GetIndicesEx() is deprecated and replaced with
a macro if Py_LIMITED_API is not set or set to the value between 0x03050400
and 0x03060000 (not including) or 0x03060100 or higher. Added functions
Expand Down
2 changes: 1 addition & 1 deletion Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ typedef struct {

#ifdef WITH_THREAD
PyThread_type_lock lock;
volatile long owner;
volatile unsigned long owner;
#endif

Py_ssize_t buffer_size;
Expand Down
2 changes: 1 addition & 1 deletion Modules/_multiprocessing/semaphore.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ enum { RECURSIVE_MUTEX, SEMAPHORE };
typedef struct {
PyObject_HEAD
SEM_HANDLE handle;
long last_tid;
unsigned long last_tid;
int count;
int maxvalue;
int kind;
Expand Down
2 changes: 1 addition & 1 deletion Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,7 @@ int pysqlite_check_thread(pysqlite_Connection* self)
if (PyThread_get_thread_ident() != self->thread_ident) {
PyErr_Format(pysqlite_ProgrammingError,
"SQLite objects created in a thread can only be used in that same thread."
"The object was created in thread id %ld and this is thread id %ld",
"The object was created in thread id %lu and this is thread id %lu",
self->thread_ident, PyThread_get_thread_ident());
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion Modules/_sqlite/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ typedef struct
int initialized;

/* thread identification of the thread the connection was created in */
long thread_ident;
unsigned long thread_ident;

pysqlite_Cache* statement_cache;

Expand Down
3 changes: 1 addition & 2 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5032,8 +5032,7 @@ static PyThread_type_lock *_ssl_locks = NULL;
static void
_ssl_threadid_callback(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id,
(unsigned long)PyThread_get_thread_ident());
CRYPTO_THREADID_set_numeric(id, PyThread_get_thread_ident());
}
#else
/* deprecated CRYPTO_set_id_callback() API. */
Expand Down
29 changes: 14 additions & 15 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ static PyTypeObject Locktype = {
typedef struct {
PyObject_HEAD
PyThread_type_lock rlock_lock;
long rlock_owner;
unsigned long rlock_owner;
unsigned long rlock_count;
PyObject *in_weakreflist;
} rlockobject;
Expand All @@ -293,7 +293,7 @@ static PyObject *
rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
{
_PyTime_t timeout;
long tid;
unsigned long tid;
PyLockStatus r = PY_LOCK_ACQUIRED;

if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
Expand Down Expand Up @@ -342,7 +342,7 @@ the lock is taken and its internal counter initialized to 1.");
static PyObject *
rlock_release(rlockobject *self)
{
long tid = PyThread_get_thread_ident();
unsigned long tid = PyThread_get_thread_ident();

if (self->rlock_count == 0 || self->rlock_owner != tid) {
PyErr_SetString(PyExc_RuntimeError,
Expand Down Expand Up @@ -371,11 +371,11 @@ to be available for other threads.");
static PyObject *
rlock_acquire_restore(rlockobject *self, PyObject *args)
{
long owner;
unsigned long owner;
unsigned long count;
int r = 1;

if (!PyArg_ParseTuple(args, "(kl):_acquire_restore", &count, &owner))
if (!PyArg_ParseTuple(args, "(kk):_acquire_restore", &count, &owner))
return NULL;

if (!PyThread_acquire_lock(self->rlock_lock, 0)) {
Expand All @@ -401,7 +401,7 @@ For internal use by `threading.Condition`.");
static PyObject *
rlock_release_save(rlockobject *self)
{
long owner;
unsigned long owner;
unsigned long count;

if (self->rlock_count == 0) {
Expand All @@ -415,7 +415,7 @@ rlock_release_save(rlockobject *self)
self->rlock_count = 0;
self->rlock_owner = 0;
PyThread_release_lock(self->rlock_lock);
return Py_BuildValue("kl", count, owner);
return Py_BuildValue("kk", count, owner);
}

PyDoc_STRVAR(rlock_release_save_doc,
Expand All @@ -427,7 +427,7 @@ For internal use by `threading.Condition`.");
static PyObject *
rlock_is_owned(rlockobject *self)
{
long tid = PyThread_get_thread_ident();
unsigned long tid = PyThread_get_thread_ident();

if (self->rlock_count > 0 && self->rlock_owner == tid) {
Py_RETURN_TRUE;
Expand Down Expand Up @@ -1031,7 +1031,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
{
PyObject *func, *args, *keyw = NULL;
struct bootstate *boot;
long ident;
unsigned long ident;

if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3,
&func, &args, &keyw))
Expand Down Expand Up @@ -1068,7 +1068,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
Py_XINCREF(keyw);
PyEval_InitThreads(); /* Start the interpreter's thread-awareness */
ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);
if (ident == -1) {
if (ident == PYTHREAD_INVALID_THREAD_ID) {
PyErr_SetString(ThreadError, "can't start new thread");
Py_DECREF(func);
Py_DECREF(args);
Expand All @@ -1077,7 +1077,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
PyMem_DEL(boot);
return NULL;
}
return PyLong_FromLong(ident);
return PyLong_FromUnsignedLong(ident);
}

PyDoc_STRVAR(start_new_doc,
Expand Down Expand Up @@ -1137,13 +1137,12 @@ information about locks.");
static PyObject *
thread_get_ident(PyObject *self)
{
long ident;
ident = PyThread_get_thread_ident();
if (ident == -1) {
unsigned long ident = PyThread_get_thread_ident();
if (ident == PYTHREAD_INVALID_THREAD_ID) {
PyErr_SetString(ThreadError, "no current thread ident");
return NULL;
}
return PyLong_FromLong(ident);
return PyLong_FromUnsignedLong(ident);
}

PyDoc_STRVAR(get_ident_doc,
Expand Down
9 changes: 5 additions & 4 deletions Modules/clinic/signalmodule.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,16 +395,17 @@ PyDoc_STRVAR(signal_pthread_kill__doc__,
{"pthread_kill", (PyCFunction)signal_pthread_kill, METH_FASTCALL, signal_pthread_kill__doc__},

static PyObject *
signal_pthread_kill_impl(PyObject *module, long thread_id, int signalnum);
signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
int signalnum);

static PyObject *
signal_pthread_kill(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
long thread_id;
unsigned long thread_id;
int signalnum;

if (!_PyArg_ParseStack(args, nargs, "li:pthread_kill",
if (!_PyArg_ParseStack(args, nargs, "ki:pthread_kill",
&thread_id, &signalnum)) {
goto exit;
}
Expand Down Expand Up @@ -463,4 +464,4 @@ signal_pthread_kill(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObjec
#ifndef SIGNAL_PTHREAD_KILL_METHODDEF
#define SIGNAL_PTHREAD_KILL_METHODDEF
#endif /* !defined(SIGNAL_PTHREAD_KILL_METHODDEF) */
/*[clinic end generated code: output=fab3dba32c058588 input=a9049054013a1b77]*/
/*[clinic end generated code: output=c1a3f374b2c77e5d input=a9049054013a1b77]*/
2 changes: 1 addition & 1 deletion Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ faulthandler_dump_traceback_later(PyObject *self,
/* Arm these locks to serve as events when released */
PyThread_acquire_lock(thread.running, 1);

if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) {
if (PyThread_start_new_thread(faulthandler_thread, NULL) == PYTHREAD_INVALID_THREAD_ID) {
PyThread_release_lock(thread.running);
Py_CLEAR(thread.file);
PyMem_Free(header);
Expand Down
9 changes: 5 additions & 4 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ module signal
#ifdef WITH_THREAD
#include <sys/types.h> /* For pid_t */
#include "pythread.h"
static long main_thread;
static unsigned long main_thread;
static pid_t main_pid;
#endif

Expand Down Expand Up @@ -1088,16 +1088,17 @@ signal_sigtimedwait_impl(PyObject *module, PyObject *sigset,
/*[clinic input]
signal.pthread_kill
thread_id: long
thread_id: unsigned_long(bitwise=True)
signalnum: int
/
Send a signal to a thread.
[clinic start generated code]*/

static PyObject *
signal_pthread_kill_impl(PyObject *module, long thread_id, int signalnum)
/*[clinic end generated code: output=2a09ce41f1c4228a input=77ed6a3b6f2a8122]*/
signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
int signalnum)
/*[clinic end generated code: output=7629919b791bc27f input=1d901f2c7bb544ff]*/
{
int err;

Expand Down
2 changes: 1 addition & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ static long dxp[256];
#include "pythread.h"

static PyThread_type_lock pending_lock = 0; /* for pending calls */
static long main_thread = 0;
static unsigned long main_thread = 0;
/* This single variable consolidates all requests to break out of the fast path
in the eval loop. */
static _Py_atomic_int eval_breaker = {0};
Expand Down
Loading

0 comments on commit aefa7eb

Please sign in to comment.