mirror of https://github.com/python/cpython.git
gh-59705: Set OS thread name when Thread.name is changed (#127702)
Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
9af96f4406
commit
c91ccbe4ac
|
@ -434,6 +434,18 @@ since it is impossible to detect the termination of alien threads.
|
||||||
Multiple threads may be given the same name. The initial name is set by
|
Multiple threads may be given the same name. The initial name is set by
|
||||||
the constructor.
|
the constructor.
|
||||||
|
|
||||||
|
On some platforms, the thread name is set at the operating system level
|
||||||
|
when the thread starts, so that it is visible in task managers.
|
||||||
|
This name may be truncated to fit in a system-specific limit (for example,
|
||||||
|
15 bytes on Linux or 63 bytes on macOS).
|
||||||
|
|
||||||
|
Changes to *name* are only reflected at the OS level when the currently
|
||||||
|
running thread is renamed. (Setting the *name* attribute of a
|
||||||
|
different thread only updates the Python Thread object.)
|
||||||
|
|
||||||
|
.. versionchanged:: 3.14
|
||||||
|
Set the operating system thread name.
|
||||||
|
|
||||||
.. method:: getName()
|
.. method:: getName()
|
||||||
setName()
|
setName()
|
||||||
|
|
||||||
|
|
|
@ -2164,6 +2164,25 @@ def work():
|
||||||
self.assertEqual(work_name, expected,
|
self.assertEqual(work_name, expected,
|
||||||
f"{len(work_name)=} and {len(expected)=}")
|
f"{len(work_name)=} and {len(expected)=}")
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(_thread, 'set_name'), "missing _thread.set_name")
|
||||||
|
@unittest.skipUnless(hasattr(_thread, '_get_name'), "missing _thread._get_name")
|
||||||
|
def test_change_name(self):
|
||||||
|
# Change the name of a thread while the thread is running
|
||||||
|
|
||||||
|
name1 = None
|
||||||
|
name2 = None
|
||||||
|
def work():
|
||||||
|
nonlocal name1, name2
|
||||||
|
name1 = _thread._get_name()
|
||||||
|
threading.current_thread().name = "new name"
|
||||||
|
name2 = _thread._get_name()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=work, name="name")
|
||||||
|
thread.start()
|
||||||
|
thread.join()
|
||||||
|
self.assertEqual(name1, "name")
|
||||||
|
self.assertEqual(name2, "new name")
|
||||||
|
|
||||||
|
|
||||||
class InterruptMainTests(unittest.TestCase):
|
class InterruptMainTests(unittest.TestCase):
|
||||||
def check_interrupt_main_with_signal_handler(self, signum):
|
def check_interrupt_main_with_signal_handler(self, signum):
|
||||||
|
|
|
@ -1026,16 +1026,20 @@ def _set_ident(self):
|
||||||
def _set_native_id(self):
|
def _set_native_id(self):
|
||||||
self._native_id = get_native_id()
|
self._native_id = get_native_id()
|
||||||
|
|
||||||
|
def _set_os_name(self):
|
||||||
|
if _set_name is None or not self._name:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
_set_name(self._name)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
def _bootstrap_inner(self):
|
def _bootstrap_inner(self):
|
||||||
try:
|
try:
|
||||||
self._set_ident()
|
self._set_ident()
|
||||||
if _HAVE_THREAD_NATIVE_ID:
|
if _HAVE_THREAD_NATIVE_ID:
|
||||||
self._set_native_id()
|
self._set_native_id()
|
||||||
if _set_name is not None and self._name:
|
self._set_os_name()
|
||||||
try:
|
|
||||||
_set_name(self._name)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
self._started.set()
|
self._started.set()
|
||||||
with _active_limbo_lock:
|
with _active_limbo_lock:
|
||||||
_active[self._ident] = self
|
_active[self._ident] = self
|
||||||
|
@ -1115,6 +1119,8 @@ def name(self):
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
assert self._initialized, "Thread.__init__() not called"
|
assert self._initialized, "Thread.__init__() not called"
|
||||||
self._name = str(name)
|
self._name = str(name)
|
||||||
|
if get_ident() == self._ident:
|
||||||
|
self._set_os_name()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ident(self):
|
def ident(self):
|
||||||
|
|
|
@ -2423,8 +2423,7 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj)
|
||||||
|
|
||||||
#ifdef PYTHREAD_NAME_MAXLEN
|
#ifdef PYTHREAD_NAME_MAXLEN
|
||||||
// Truncate to PYTHREAD_NAME_MAXLEN bytes + the NUL byte if needed
|
// Truncate to PYTHREAD_NAME_MAXLEN bytes + the NUL byte if needed
|
||||||
size_t len = PyBytes_GET_SIZE(name_encoded);
|
if (PyBytes_GET_SIZE(name_encoded) > PYTHREAD_NAME_MAXLEN) {
|
||||||
if (len > PYTHREAD_NAME_MAXLEN) {
|
|
||||||
PyObject *truncated;
|
PyObject *truncated;
|
||||||
truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded),
|
truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded),
|
||||||
PYTHREAD_NAME_MAXLEN);
|
PYTHREAD_NAME_MAXLEN);
|
||||||
|
|
Loading…
Reference in New Issue