diff --git a/Include/pythread.h b/Include/pythread.h index f22e8c42c50277..decc94339e045d 100644 --- a/Include/pythread.h +++ b/Include/pythread.h @@ -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); diff --git a/Lib/threading.py b/Lib/threading.py index cec9cdb8e6985f..98bf7e13f01af9 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -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 @@ -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) self._started.set() with _active_limbo_lock: _active[self._ident] = self diff --git a/Misc/NEWS.d/next/Library/2019-07-03-17-31-07.bpo-15500.Ey3JkG.rst b/Misc/NEWS.d/next/Library/2019-07-03-17-31-07.bpo-15500.Ey3JkG.rst new file mode 100644 index 00000000000000..704685dcab90b8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-03-17-31-07.bpo-15500.Ey3JkG.rst @@ -0,0 +1 @@ +Export :class:`threading.Thread` names to the OS. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 9ab8e7a0ceb3c4..80d281c667cb22 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -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)) { @@ -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 */ }; diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index 07ea08b1750d53..973f7324fac5b2 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -2,6 +2,37 @@ preserve [clinic start generated code]*/ +#if defined(PY_HAVE_SET_THREAD_NAME) + +PyDoc_STRVAR(_thread__set_thread_name__doc__, +"_set_thread_name($module, name, /)\n" +"--\n" +"\n" +"Set the name of the current thread."); + +#define _THREAD__SET_THREAD_NAME_METHODDEF \ + {"_set_thread_name", (PyCFunction)_thread__set_thread_name, METH_O, _thread__set_thread_name__doc__}, + +static PyObject * +_thread__set_thread_name_impl(PyObject *module, PyObject *name); + +static PyObject * +_thread__set_thread_name(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *name; + + if (!PyUnicode_FSConverter(arg, &name)) { + goto exit; + } + return_value = _thread__set_thread_name_impl(module, name); + +exit: + return return_value; +} + +#endif /* defined(PY_HAVE_SET_THREAD_NAME) */ + PyDoc_STRVAR(_thread__is_main_interpreter__doc__, "_is_main_interpreter($module, /)\n" "--\n" @@ -19,4 +50,8 @@ _thread__is_main_interpreter(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _thread__is_main_interpreter_impl(module); } -/*[clinic end generated code: output=505840d1b9101789 input=a9049054013a1b77]*/ + +#ifndef _THREAD__SET_THREAD_NAME_METHODDEF + #define _THREAD__SET_THREAD_NAME_METHODDEF +#endif /* !defined(_THREAD__SET_THREAD_NAME_METHODDEF) */ +/*[clinic end generated code: output=39d6d81a301f0458 input=a9049054013a1b77]*/ diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index a36d16c19eade5..c244c9aab28b7a 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -17,6 +17,7 @@ #elif defined(__FreeBSD__) # include /* pthread_getthreadid_np() */ #elif defined(__OpenBSD__) +# include # include /* getthrid() */ #elif defined(_AIX) # include /* thread_self() */ @@ -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); +#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) {