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
.. 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
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.
+ This is a non-negative integer, or ``None`` if the thread has not
+ been started. 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 (until the thread terminates,
+ after which the value may be recycled by the OS).
+
+ .. 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.
PyAPI_FUNC(void) _Py_NO_RETURN PyThread_exit_thread(void);
PyAPI_FUNC(unsigned long) PyThread_get_thread_ident(void);
+#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32)
+#define PY_HAVE_THREAD_NATIVE_ID
+PyAPI_FUNC(unsigned long) PyThread_get_thread_native_id(void);
+#endif
+
PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void);
PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock);
PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
self.assertRegex(repr(t), r'^<TestThread\(.*, initial\)>$')
t.start()
+ if hasattr(threading, 'get_native_id'):
+ 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:
_allocate_lock = _thread.allocate_lock
_set_sentinel = _thread._set_sentinel
get_ident = _thread.get_ident
+try:
+ get_native_id = _thread.get_native_id
+ _HAVE_THREAD_NATIVE_ID = True
+ __all__.append('get_native_id')
+except AttributeError:
+ _HAVE_THREAD_NATIVE_ID = False
ThreadError = _thread.error
try:
_CRLock = _thread.RLock
else:
self._daemonic = current_thread().daemon
self._ident = None
+ if _HAVE_THREAD_NATIVE_ID:
+ self._native_id = None
self._tstate_lock = None
self._started = Event()
self._is_stopped = False
def _set_ident(self):
self._ident = get_ident()
+ if _HAVE_THREAD_NATIVE_ID:
+ 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
try:
self._set_ident()
self._set_tstate_lock()
+ if _HAVE_THREAD_NATIVE_ID:
+ self._set_native_id()
self._started.set()
with _active_limbo_lock:
_active[self._ident] = self
assert self._initialized, "Thread.__init__() not called"
return self._ident
+ if _HAVE_THREAD_NATIVE_ID:
+ @property
+ def native_id(self):
+ """Native integral thread ID of this thread, or None 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.
self._set_tstate_lock()
self._started.set()
self._set_ident()
+ if _HAVE_THREAD_NATIVE_ID:
+ self._set_native_id()
with _active_limbo_lock:
_active[self._ident] = self
self._started.set()
self._set_ident()
+ if _HAVE_THREAD_NATIVE_ID:
+ self._set_native_id()
with _active_limbo_lock:
_active[self._ident] = self
--- /dev/null
+Add native thread ID (TID) to threading.Thread objects (supported platforms: Windows, FreeBSD, Linux, macOS)
\ No newline at end of file
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.");
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+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.");
+#endif
+
static PyObject *
thread__count(PyObject *self, PyObject *Py_UNUSED(ignored))
{
METH_NOARGS, interrupt_doc},
{"get_ident", thread_get_ident,
METH_NOARGS, get_ident_doc},
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+ {"get_native_id", thread_get_native_id,
+ METH_NOARGS, get_native_id_doc},
+#endif
{"_count", thread__count,
METH_NOARGS, _count_doc},
{"stack_size", (PyCFunction)thread_stack_size,
unsigned long PyThread_get_thread_ident(void);
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+unsigned long PyThread_get_thread_native_id(void);
+#endif
+
/*
* Initialization of the C package, should not be needed.
*/
return GetCurrentThreadId();
}
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+/*
+ * 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();
+ }
+
+ DWORD native_id;
+ native_id = GetCurrentThreadId();
+ return (unsigned long) native_id;
+}
+#endif
+
void _Py_NO_RETURN
PyThread_exit_thread(void)
{
#endif
#include <signal.h>
+#if defined(__linux__)
+# include <sys/syscall.h> /* syscall(SYS_gettid) */
+#elif defined(__FreeBSD__)
+# include <pthread_np.h> /* pthread_getthreadid_np() */
+#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
return (unsigned long) threadid;
}
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+unsigned long
+PyThread_get_thread_native_id(void)
+{
+ if (!initialized)
+ PyThread_init_thread();
+#ifdef __APPLE__
+ uint64_t native_id;
+ (void) pthread_threadid_np(NULL, &native_id);
+#elif defined(__linux__)
+ pid_t native_id;
+ native_id = syscall(SYS_gettid);
+#elif defined(__FreeBSD__)
+ int native_id;
+ native_id = pthread_getthreadid_np();
+#endif
+ return (unsigned long) native_id;
+}
+#endif
+
void _Py_NO_RETURN
PyThread_exit_thread(void)
{