Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
523f10b
Added gettid() functionality to determine kernel thread ID; Thread ob…
jaketesler Feb 22, 2019
8babc6a
Removed unnecessary #include statements
jaketesler Feb 22, 2019
80d02f8
Added/improved documentation for new functionality
jaketesler Feb 22, 2019
675f600
Updated inline and formal documentation
jaketesler Feb 22, 2019
e4506af
Error fix: added dummy get_tid() to _dummy_threading
jaketesler Feb 22, 2019
1e5fec7
Added get_thread_id to thread_nt.h as Windows placeholder
jaketesler Feb 22, 2019
4f6dd65
📜🤖 Added by blurb_it.
blurb-it[bot] Feb 22, 2019
8646174
Added TID functionality for Windows
jaketesler Feb 28, 2019
0c42362
Updated documentation to remove references to Unix- and Apple-specifi…
jaketesler Feb 28, 2019
8de952e
Merge branch 'feature-threadid' of https://github.com/jaketesler/cpyt…
jaketesler Feb 28, 2019
a358c13
Fixed undeclared variable in else clause
jaketesler Feb 28, 2019
f57b5db
Added TID assertion tests
jaketesler May 9, 2019
4362dbc
Resolve conflicts with `master` branch
jaketesler May 9, 2019
b259357
Merge branch 'master' into feature-threadid
jaketesler May 9, 2019
658217c
Added FreeBSD support
jaketesler May 10, 2019
9d0d318
Merge branch 'feature-threadid' of https://github.com/jaketesler/cpyt…
jaketesler May 10, 2019
a19d208
Updated documentation for low-level `_thread` threading API.
jaketesler May 11, 2019
e0e801b
Implemented changes per suggestions from @pitrou.
jaketesler May 11, 2019
18cd36f
Update test_threading.py
jaketesler May 11, 2019
9ce0de2
Improve availability wording in doc
pitrou May 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Doc/library/_thread.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ This module defines the following constants and functions:
may be recycled when a thread exits and another thread is created.


.. function:: get_native_id()

Return the native integral Thread ID of the current thread assigned by the kernel.
This is a non-negative integer.
Its value may be used to uniquely identify this particular thread system-wide
(until the thread terminates, after which the value may be recycled by the OS).

.. availability:: Windows, FreeBSD, Linux, macOS.

.. versionadded:: 3.8


.. function:: stack_size([size])

Return the thread stack size used when creating new threads. The optional
Expand Down
31 changes: 31 additions & 0 deletions Doc/library/threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ This module defines the following functions:
.. versionadded:: 3.3


.. function:: get_native_id()

Return the native integral Thread ID of the current thread assigned by the kernel.
This is a non-negative integer.
Its value may be used to uniquely identify this particular thread system-wide
(until the thread terminates, after which the value may be recycled by the OS).

.. availability:: Windows, FreeBSD, Linux, macOS.

.. versionadded:: 3.8


.. function:: enumerate()

Return a list of all :class:`Thread` objects currently alive. The list
Expand Down Expand Up @@ -297,6 +309,25 @@ since it is impossible to detect the termination of alien threads.
another thread is created. The identifier is available even after the
thread has exited.

.. attribute:: native_id

The native integral thread ID of this thread or ``0`` if the thread has not
been started. This is a non-negative integer. See the
:func:`get_native_id` function.
This represents the Thread ID (``TID``) as assigned to the
thread by the OS (kernel). Its value may be used to uniquely identify
this particular thread system-wide.

.. note::

Similar to Process IDs, Thread IDs are only valid (guaranteed unique
system-wide) from the time the thread is created until the thread
has been terminated.

.. availability:: Windows, FreeBSD, Linux, macOS.

.. versionadded:: 3.8

.. method:: is_alive()

Return whether the thread is alive.
Expand Down
1 change: 1 addition & 0 deletions Include/pythread.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ PyAPI_FUNC(void) PyThread_init_thread(void);
PyAPI_FUNC(unsigned long) PyThread_start_new_thread(void (*)(void *), void *);
PyAPI_FUNC(void) _Py_NO_RETURN PyThread_exit_thread(void);
PyAPI_FUNC(unsigned long) PyThread_get_thread_ident(void);
PyAPI_FUNC(unsigned long) PyThread_get_thread_native_id(void);

PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void);
PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock);
Expand Down
4 changes: 4 additions & 0 deletions Lib/_dummy_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def get_ident():
"""
return 1

def get_native_id():
"""Dummy implementation of _thread.get_native_id()."""
return 0

def allocate_lock():
"""Dummy implementation of _thread.allocate_lock()."""
return LockType()
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def test_various_ops(self):
self.assertRegex(repr(t), r'^<TestThread\(.*, initial\)>$')
t.start()

native_ids = set(t.native_id for t in threads) | {threading.get_native_id()}
self.assertNotIn(None, native_ids)
self.assertEqual(len(native_ids), NUMTASKS + 1)

if verbose:
print('waiting for all tasks to complete')
for t in threads:
Expand Down
23 changes: 21 additions & 2 deletions Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
# with the multiprocessing module, which doesn't provide the old
# Java inspired names.

__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
'enumerate', 'main_thread', 'TIMEOUT_MAX',
__all__ = ['get_ident', 'get_native_id', 'active_count', 'Condition',
'current_thread', 'enumerate', 'main_thread', 'TIMEOUT_MAX',
'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
'Barrier', 'BrokenBarrierError', 'Timer', 'ThreadError',
'setprofile', 'settrace', 'local', 'stack_size']
Expand All @@ -34,6 +34,7 @@
_allocate_lock = _thread.allocate_lock
_set_sentinel = _thread._set_sentinel
get_ident = _thread.get_ident
get_native_id = _thread.get_native_id
ThreadError = _thread.error
try:
_CRLock = _thread.RLock
Expand Down Expand Up @@ -790,6 +791,7 @@ class is implemented.
else:
self._daemonic = current_thread().daemon
self._ident = None
self._native_id = 0
self._tstate_lock = None
self._started = Event()
self._is_stopped = False
Expand Down Expand Up @@ -891,6 +893,9 @@ def _bootstrap(self):
def _set_ident(self):
self._ident = get_ident()

def _set_native_id(self):
self._native_id = get_native_id()

def _set_tstate_lock(self):
"""
Set a lock object which will be released by the interpreter when
Expand All @@ -903,6 +908,7 @@ def _bootstrap_inner(self):
try:
self._set_ident()
self._set_tstate_lock()
self._set_native_id()
self._started.set()
with _active_limbo_lock:
_active[self._ident] = self
Expand Down Expand Up @@ -1077,6 +1083,17 @@ def ident(self):
assert self._initialized, "Thread.__init__() not called"
return self._ident

@property
def native_id(self):
"""Native integral thread ID of this thread or 0 if it has not been started.

This is a non-negative integer. See the get_native_id() function.
This represents the Thread ID as reported by the kernel.

"""
assert self._initialized, "Thread.__init__() not called"
return self._native_id

def is_alive(self):
"""Return whether the thread is alive.

Expand Down Expand Up @@ -1176,6 +1193,7 @@ def __init__(self):
self._set_tstate_lock()
self._started.set()
self._set_ident()
self._set_native_id()
with _active_limbo_lock:
_active[self._ident] = self

Expand All @@ -1195,6 +1213,7 @@ def __init__(self):

self._started.set()
self._set_ident()
self._set_native_id()
with _active_limbo_lock:
_active[self._ident] = self

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add native thread ID (TID) to threading.Thread objects
16 changes: 16 additions & 0 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,20 @@ allocated consecutive numbers starting at 1, this behavior should not\n\
be relied upon, and the number should be seen purely as a magic cookie.\n\
A thread's identity may be reused for another thread after it exits.");

static PyObject *
thread_get_native_id(PyObject *self, PyObject *Py_UNUSED(ignored))
{
unsigned long native_id = PyThread_get_thread_native_id();
return PyLong_FromUnsignedLong(native_id);
}

PyDoc_STRVAR(get_native_id_doc,
"get_native_id() -> integer\n\
\n\
Return a non-negative integer identifying the thread as reported\n\
by the OS (kernel). This may be used to uniquely identify a\n\
particular thread within a system.");

static PyObject *
thread__count(PyObject *self, PyObject *Py_UNUSED(ignored))
{
Expand Down Expand Up @@ -1310,6 +1324,8 @@ static PyMethodDef thread_methods[] = {
METH_NOARGS, interrupt_doc},
{"get_ident", thread_get_ident,
METH_NOARGS, get_ident_doc},
{"get_native_id", thread_get_native_id,
METH_NOARGS, get_native_id_doc},
{"_count", thread__count,
METH_NOARGS, _count_doc},
{"stack_size", (PyCFunction)thread_stack_size,
Expand Down
16 changes: 16 additions & 0 deletions Python/thread_nt.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ LeaveNonRecursiveMutex(PNRMUTEX mutex)

unsigned long PyThread_get_thread_ident(void);

unsigned long PyThread_get_thread_native_id(void);

/*
* Initialization of the C package, should not be needed.
*/
Expand Down Expand Up @@ -227,6 +229,20 @@ PyThread_get_thread_ident(void)
return GetCurrentThreadId();
}

/*
* Return the native Thread ID (TID) of the calling thread.
* The native ID of a thread is valid and guaranteed to be unique system-wide
* from the time the thread is created until the thread has been terminated.
*/
unsigned long
PyThread_get_thread_native_id(void)
{
if (!initialized)
PyThread_init_thread();

return GetCurrentThreadId();
}

void _Py_NO_RETURN
PyThread_exit_thread(void)
{
Expand Down
27 changes: 27 additions & 0 deletions Python/thread_pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
#endif
#include <signal.h>

#if defined(__linux__)
#include <sys/syscall.h>
#elif defined(__FreeBSD__)
#include <pthread_np.h>
#endif

/* The POSIX spec requires that use of pthread_attr_setstacksize
be conditional on _POSIX_THREAD_ATTR_STACKSIZE being defined. */
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
Expand Down Expand Up @@ -302,6 +308,27 @@ PyThread_get_thread_ident(void)
return (unsigned long) threadid;
}

unsigned long
PyThread_get_thread_native_id(void)
{
if (!initialized)
PyThread_init_thread();
#ifdef __APPLE__
uint64_t native_id;
pthread_threadid_np(NULL, &native_id);
#elif defined(__linux__)
pid_t native_id;
native_id = syscall(__NR_gettid);
#elif defined(__FreeBSD__)
pid_t native_id;
native_id = pthread_getthreadid_np();
#else
unsigned long native_id;
native_id = 0;
#endif
return (unsigned long) native_id;
}

void _Py_NO_RETURN
PyThread_exit_thread(void)
{
Expand Down