Skip to content

gh-59705: Export threading.Thread() names to the OS #14578

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions Include/pythread.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ PyAPI_FUNC(unsigned long) PyThread_get_thread_ident(void);
PyAPI_FUNC(unsigned long) PyThread_get_thread_native_id(void);
#endif

#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__NetBSD__)
#define PY_HAVE_SET_THREAD_NAME
PyAPI_FUNC(int) PyThread_set_thread_name(const char *);
#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);
Expand Down
7 changes: 7 additions & 0 deletions Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
__all__.append('get_native_id')
except AttributeError:
_HAVE_THREAD_NATIVE_ID = False
try:
_set_thread_name = _thread._set_thread_name
_HAVE_SET_THREAD_NAME = True
except AttributeError:
_HAVE_SET_THREAD_NAME = False
ThreadError = _thread.error
try:
_CRLock = _thread.RLock
Expand Down Expand Up @@ -924,6 +929,8 @@ def _bootstrap_inner(self):
self._set_tstate_lock()
if _HAVE_THREAD_NATIVE_ID:
self._set_native_id()
if _HAVE_SET_THREAD_NAME:
_set_thread_name(self.name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be more sophisticated, e.g. f"{sys.executable} [thread {self.name!r}]"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't work well for platforms with the 16-character limitation.

self._started.set()
with _active_limbo_lock:
_active[self._ident] = self
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Export :class:`threading.Thread` names to the OS.
25 changes: 25 additions & 0 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,30 @@ function returns; the return value is ignored. The thread will also exit\n\
when the function raises an unhandled exception; a stack trace will be\n\
printed unless the exception is SystemExit.\n");

#ifdef PY_HAVE_SET_THREAD_NAME
/*[clinic input]
_thread._set_thread_name

name: object(converter="PyUnicode_FSConverter")
/

Set the name of the current thread.
[clinic start generated code]*/

static PyObject *
_thread__set_thread_name_impl(PyObject *module, PyObject *name)
/*[clinic end generated code: output=72d978d5c53e2762 input=8c46f838fc1040c2]*/
{
if (PyThread_set_thread_name(PyBytes_AS_STRING(name))) {
Py_DECREF(name);
PyErr_SetString(ThreadError, "setting the thread name failed");
return NULL;
}
Py_DECREF(name);
Py_RETURN_NONE;
}
#endif

static PyObject *
thread_PyThread_exit_thread(PyObject *self, PyObject *Py_UNUSED(ignored))
{
Expand Down Expand Up @@ -1495,6 +1519,7 @@ static PyMethodDef thread_methods[] = {
{"_excepthook", thread_excepthook,
METH_O, excepthook_doc},
_THREAD__IS_MAIN_INTERPRETER_METHODDEF
_THREAD__SET_THREAD_NAME_METHODDEF
{NULL, NULL} /* sentinel */
};

Expand Down
37 changes: 36 additions & 1 deletion Modules/clinic/_threadmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions Python/thread_pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#elif defined(__FreeBSD__)
# include <pthread_np.h> /* pthread_getthreadid_np() */
#elif defined(__OpenBSD__)
# include <pthread_np.h>
# include <unistd.h> /* getthrid() */
#elif defined(_AIX)
# include <sys/thread.h> /* thread_self() */
Expand Down Expand Up @@ -343,6 +344,37 @@ PyThread_get_thread_native_id(void)
}
#endif

#ifdef PY_HAVE_SET_THREAD_NAME
int
PyThread_set_thread_name(const char *name)
{
if (!initialized) {
PyThread_init_thread();
}
// Truncate the string to 15 chars for pthread_setname_np().
char buf[16];
size_t len = Py_ARRAY_LENGTH(buf) - 1;
strncpy(buf, name, len);
buf[len] = '\0';
#ifndef __APPLE__
unsigned long ident = PyThread_get_thread_ident();
#endif
int ret = 0;
#ifdef __APPLE__
// On macOS, pthread_setname_np() can only set the calling thread's name.
ret = pthread_setname_np(buf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems (from the man page) that macOS doesn't have the 16-character limitation. Could we pass the original buffer here?

#elif defined(__FreeBSD__) || defined(__OpenBSD__)
// No return value.
pthread_set_name_np((pthread_t)ident, buf);
#elif defined(__NetBSD__)
ret = pthread_setname_np((pthread_t)ident, "%s", (void *)buf);
#elif defined(__linux__)
ret = pthread_setname_np((pthread_t)ident, buf);
#endif
return ret;
}
#endif

void _Py_NO_RETURN
PyThread_exit_thread(void)
{
Expand Down