Skip to content

Commit d7a47bf

Browse files
committed
test
1 parent 95d289f commit d7a47bf

File tree

2 files changed

+79
-94
lines changed

2 files changed

+79
-94
lines changed

Lib/test/test_threading.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2359,20 +2359,13 @@ def work():
23592359
with self.subTest(name=name, expected=expected):
23602360
work_name = None
23612361
thread = threading.Thread(target=work, name=name)
2362-
try:
2363-
thread.start()
2364-
thread.join()
2365-
# If the name is non-ASCII and the result is empty, skip (platform limitation)
2366-
if any(ord(c) > 127 for c in name) and (not work_name or work_name == ""):
2367-
self.skipTest(f"Platform does not support non-ASCII thread names: got empty name for {name!r}")
2368-
self.assertEqual(work_name, expected,
2369-
f"{len(work_name)=} and {len(expected)=}")
2370-
except OSError as exc:
2371-
# Accept EINVAL (22) for non-ASCII names on platforms that do not support them
2372-
if getattr(exc, 'errno', None) == 22 and any(ord(c) > 127 for c in name):
2373-
self.skipTest(f"Platform does not support non-ASCII thread names: {exc}")
2374-
else:
2375-
raise
2362+
thread.start()
2363+
thread.join()
2364+
# If the name is non-ASCII and the result is empty, skip (platform limitation)
2365+
if any(ord(c) > 127 for c in name) and (not work_name or work_name == ""):
2366+
self.skipTest(f"Platform does not support non-ASCII thread names: got empty name for {name!r}")
2367+
self.assertEqual(work_name, expected,
2368+
f"{len(work_name)=} and {len(expected)=}")
23762369

23772370
@unittest.skipUnless(hasattr(_thread, 'set_name'), "missing _thread.set_name")
23782371
@unittest.skipUnless(hasattr(_thread, '_get_name'), "missing _thread._get_name")

Modules/_threadmodule.c

Lines changed: 72 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
#ifdef HAVE_SIGNAL_H
1818
# include <signal.h> // SIGINT
1919
#endif
20+
#ifdef HAVE_PTHREAD_H
21+
# include <pthread.h>
22+
#endif
23+
#include <errno.h>
2024

2125
// ThreadError is just an alias to PyExc_RuntimeError
2226
#define ThreadError PyExc_RuntimeError
@@ -71,22 +75,59 @@ get_thread_state_by_cls(PyTypeObject *cls)
7175
return get_thread_state(module);
7276
}
7377

78+
// Helper to set the thread name using platform-specific APIs
79+
static int
80+
set_native_thread_name(const char *name)
81+
{
82+
#ifdef __APPLE__
83+
return pthread_setname_np(name);
84+
#elif defined(__NetBSD__)
85+
pthread_t thread = pthread_self();
86+
return pthread_setname_np(thread, "%s", (void *)name);
87+
#elif defined(HAVE_PTHREAD_SETNAME_NP)
88+
pthread_t thread = pthread_self();
89+
return pthread_setname_np(thread, name);
90+
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
91+
pthread_t thread = pthread_self();
92+
pthread_set_name_np(thread, name);
93+
return 0; /* pthread_set_name_np() returns void */
94+
#else
95+
return 0;
96+
#endif
97+
}
98+
99+
// Helper to encode and truncate thread name
100+
static PyObject *
101+
encode_thread_name(PyObject *name_obj, const char *encoding)
102+
{
103+
#ifdef __sun
104+
// Solaris always uses UTF-8
105+
encoding = "utf-8";
106+
#endif
107+
PyObject *name_encoded = PyUnicode_AsEncodedString(name_obj, encoding, "replace");
108+
if (name_encoded == NULL) {
109+
return NULL;
110+
}
111+
#ifdef _PYTHREAD_NAME_MAXLEN
112+
if (PyBytes_GET_SIZE(name_encoded) > _PYTHREAD_NAME_MAXLEN) {
113+
PyObject *truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded), _PYTHREAD_NAME_MAXLEN);
114+
if (truncated == NULL) {
115+
Py_DECREF(name_encoded);
116+
return NULL;
117+
}
118+
Py_SETREF(name_encoded, truncated);
119+
}
120+
#endif
121+
return name_encoded;
122+
}
123+
74124
#ifdef MS_WINDOWS
75125
typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*);
76126
typedef HRESULT (WINAPI *PF_SET_THREAD_DESCRIPTION)(HANDLE, PCWSTR);
77127
static PF_GET_THREAD_DESCRIPTION pGetThreadDescription = NULL;
78128
static PF_SET_THREAD_DESCRIPTION pSetThreadDescription = NULL;
79129
#endif
80130

81-
#if defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP)
82-
static int _set_thread_name(const char *name);
83-
#endif
84-
85-
// Fallback: Provides a no-op implementation if neither pthread naming API is available. This avoids linker errors and provides a portable stub.
86-
#if !defined(HAVE_PTHREAD_SETNAME_NP) && !defined(HAVE_PTHREAD_SET_NAME_NP)
87-
static int _set_thread_name(const char *name) { return 0; }
88-
#endif
89-
90131

91132
/*[clinic input]
92133
module _thread
@@ -2584,100 +2625,52 @@ _thread.set_name
25842625
Set the name of the current thread.
25852626
[clinic start generated code]*/
25862627

2587-
2588-
#ifndef MS_WINDOWS
2589-
// Helper to set the thread name using platform-specific APIs (POSIX only)
2590-
static int
2591-
_set_thread_name(const char *name)
2592-
{
2593-
int rc;
2594-
#ifdef __APPLE__
2595-
rc = pthread_setname_np(name);
2596-
#elif defined(__NetBSD__)
2597-
pthread_t thread = pthread_self();
2598-
rc = pthread_setname_np(thread, "%s", (void *)name);
2599-
#elif defined(HAVE_PTHREAD_SETNAME_NP)
2600-
pthread_t thread = pthread_self();
2601-
rc = pthread_setname_np(thread, name);
2602-
#else /* defined(HAVE_PTHREAD_SET_NAME_NP) */
2603-
pthread_t thread = pthread_self();
2604-
rc = 0; /* pthread_set_name_np() returns void */
2605-
pthread_set_name_np(thread, name);
2606-
#endif
2607-
return rc;
2608-
}
2609-
#endif // !MS_WINDOWS
2610-
2611-
26122628
static PyObject *
26132629
_thread_set_name_impl(PyObject *module, PyObject *name_obj)
26142630
/*[clinic end generated code: output=402b0c68e0c0daed input=7e7acd98261be82f]*/
26152631
{
26162632
#ifndef MS_WINDOWS
2617-
// POSIX and non-Windows platforms
2618-
#ifdef __sun
2619-
const char *encoding = "utf-8";
2620-
#else
26212633
PyInterpreterState *interp = _PyInterpreterState_GET();
26222634
const char *encoding = interp->unicode.fs_codec.encoding;
2623-
#endif
2624-
PyObject *name_encoded;
2625-
int rc;
2626-
2627-
name_encoded = PyUnicode_AsEncodedString(name_obj, encoding, "replace");
2635+
PyObject *name_encoded = encode_thread_name(name_obj, encoding);
26282636
if (name_encoded == NULL) {
26292637
return NULL;
26302638
}
2631-
#ifdef _PYTHREAD_NAME_MAXLEN
2632-
if (PyBytes_GET_SIZE(name_encoded) > _PYTHREAD_NAME_MAXLEN) {
2633-
PyObject *truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded), _PYTHREAD_NAME_MAXLEN);
2634-
if (truncated == NULL) {
2635-
Py_DECREF(name_encoded);
2636-
return NULL;
2637-
}
2638-
Py_SETREF(name_encoded, truncated);
2639-
}
2640-
#endif
26412639
const char *name = PyBytes_AS_STRING(name_encoded);
2642-
rc = _set_thread_name(name);
2643-
Py_DECREF(name_encoded);
2644-
2645-
// Fallback: If EINVAL, try ASCII encoding with "replace"
2646-
if (rc == EINVAL) {
2647-
name_encoded = PyUnicode_AsEncodedString(name_obj, "ascii", "replace");
2648-
if (name_encoded == NULL) {
2649-
return NULL;
2650-
}
2651-
#ifdef _PYTHREAD_NAME_MAXLEN
2652-
if (PyBytes_GET_SIZE(name_encoded) > _PYTHREAD_NAME_MAXLEN) {
2653-
PyObject *truncated = PyBytes_FromStringAndSize(PyBytes_AS_STRING(name_encoded), _PYTHREAD_NAME_MAXLEN);
2654-
if (truncated == NULL) {
2655-
Py_DECREF(name_encoded);
2640+
int rc = set_native_thread_name(name);
2641+
if (rc) {
2642+
int err = rc;
2643+
Py_DECREF(name_encoded);
2644+
if (err == EINVAL && strcmp(encoding, "ascii") != 0) {
2645+
// Retry with ASCII encoding and 'replace' if not already ASCII
2646+
name_encoded = encode_thread_name(name_obj, "ascii");
2647+
if (name_encoded == NULL) {
26562648
return NULL;
26572649
}
2658-
Py_SETREF(name_encoded, truncated);
2650+
name = PyBytes_AS_STRING(name_encoded);
2651+
rc = set_native_thread_name(name);
2652+
if (rc) {
2653+
err = rc;
2654+
Py_DECREF(name_encoded);
2655+
errno = err;
2656+
return PyErr_SetFromErrno(PyExc_OSError);
2657+
}
2658+
Py_DECREF(name_encoded);
2659+
Py_RETURN_NONE;
26592660
}
2660-
#endif
2661-
name = PyBytes_AS_STRING(name_encoded);
2662-
rc = _set_thread_name(name);
2663-
Py_DECREF(name_encoded);
2664-
}
2665-
2666-
if (rc) {
2667-
errno = rc;
2661+
errno = err;
26682662
return PyErr_SetFromErrno(PyExc_OSError);
26692663
}
2664+
Py_DECREF(name_encoded);
26702665
Py_RETURN_NONE;
26712666
#else
26722667
// Windows implementation
26732668
assert(pSetThreadDescription != NULL);
2674-
26752669
Py_ssize_t len;
26762670
wchar_t *name = PyUnicode_AsWideCharString(name_obj, &len);
26772671
if (name == NULL) {
26782672
return NULL;
26792673
}
2680-
26812674
if (len > _PYTHREAD_NAME_MAXLEN) {
26822675
// Truncate the name
26832676
Py_UCS4 ch = name[_PYTHREAD_NAME_MAXLEN-1];
@@ -2688,7 +2681,6 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj)
26882681
name[_PYTHREAD_NAME_MAXLEN] = 0;
26892682
}
26902683
}
2691-
26922684
HRESULT hr = pSetThreadDescription(GetCurrentThread(), name);
26932685
PyMem_Free(name);
26942686
if (FAILED(hr)) {
@@ -2932,4 +2924,4 @@ PyMODINIT_FUNC
29322924
PyInit__thread(void)
29332925
{
29342926
return PyModuleDef_Init(&thread_module);
2935-
}
2927+
}

0 commit comments

Comments
 (0)