Use native code for setting thread names

Fixes #924 (Fixed exception not caught in Startup.py thrown by "pthread_setname_np" on Linux)
This commit is contained in:
Kovid Goyal 2019-01-27 21:07:03 +05:30
parent 57a68ad841
commit 0bd223a88a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 73 additions and 33 deletions

View File

@ -17,7 +17,7 @@ __builtin__.__dict__['_'] = lambda s: s
# immediately translated to the environment language
__builtin__.__dict__['__'] = lambda s: s
from calibre.constants import iswindows, preferred_encoding, plugins, isosx, islinux, isfrozen, DEBUG
from calibre.constants import iswindows, preferred_encoding, plugins, isosx, islinux, isfrozen, DEBUG, isfreebsd
_run_once = False
winutil = winutilerror = None
@ -172,36 +172,28 @@ if not _run_once:
bound_signal.connect(slot, **kw)
__builtin__.__dict__['connect_lambda'] = connect_lambda
if islinux:
if islinux or isosx or isfreebsd:
# Name all threads at the OS level created using the threading module, see
# http://bugs.python.org/issue15500
import ctypes, ctypes.util, threading
libpthread_path = ctypes.util.find_library("pthread")
if libpthread_path:
libpthread = ctypes.CDLL(libpthread_path)
if hasattr(libpthread, "pthread_setname_np"):
pthread_setname_np = libpthread.pthread_setname_np
pthread_setname_np.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
pthread_setname_np.restype = ctypes.c_int
orig_start = threading.Thread.start
import threading
def new_start(self):
orig_start(self)
try:
orig_start = threading.Thread.start
def new_start(self):
orig_start(self)
try:
name = self.name
if not name or name.startswith('Thread-'):
name = self.__class__.__name__
if name == 'Thread':
name = self.name
if not name or name.startswith('Thread-'):
name = self.__class__.__name__
if name == 'Thread':
name = self.name
if name:
if isinstance(name, unicode):
name = name.encode('ascii', 'replace')
ident = getattr(self, "ident", None)
if ident is not None:
pthread_setname_np(ident, name[:15])
except Exception:
pass # Don't care about failure to set name
threading.Thread.start = new_start
if name:
if isinstance(name, unicode):
name = name.encode('ascii', 'replace').decode('ascii')
plugins['speedup'][0].set_thread_name(name[:15])
except Exception:
pass # Don't care about failure to set name
threading.Thread.start = new_start
def test_lopen():

View File

@ -1,10 +1,12 @@
#define UNICODE
#include <Python.h>
#include <datetime.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <pthread.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
@ -42,7 +44,7 @@ speedup_parse_date(PyObject *self, PyObject *args) {
year = strtol(raw, &end, 10);
if ((end - raw) != 4) Py_RETURN_NONE;
raw += 5;
month = strtol(raw, &end, 10);
if ((end - raw) != 2) Py_RETURN_NONE;
@ -51,7 +53,7 @@ speedup_parse_date(PyObject *self, PyObject *args) {
day = strtol(raw, &end, 10);
if ((end - raw) != 2) Py_RETURN_NONE;
raw += 3;
hour = strtol(raw, &end, 10);
if ((end - raw) != 2) Py_RETURN_NONE;
raw += 3;
@ -88,7 +90,7 @@ static PyObject*
speedup_pdf_float(PyObject *self, PyObject *args) {
double f = 0.0, a = 0.0;
char *buf = "0", *dot;
void *free_buf = NULL;
void *free_buf = NULL;
int precision = 6, l = 0;
PyObject *ret;
@ -215,7 +217,7 @@ speedup_create_texture(PyObject *self, PyObject *args, PyObject *kw) {
}
}
// Create the texture in PPM (P6) format
// Create the texture in PPM (P6) format
memcpy(ppm, header, strlen(header));
t = ppm + strlen(header);
for (i = 0, j = 0; j < width * height; i += 3, j += 1) {
@ -340,7 +342,7 @@ clean_xml_chars(PyObject *self, PyObject *text) {
for (; i < PyUnicode_GET_SIZE(text); i++) {
ch = PyUnicode_AS_UNICODE(text)[i];
#ifdef Py_UNICODE_WIDE
if ((0x20 <= ch && ch <= 0xd7ff && ch != 0x7f) || ch == 9 || ch == 10 || ch == 13 || (0xe000 <= ch && ch <= 0xfffd) || (0xffff < ch && ch <= 0x10ffff))
if ((0x20 <= ch && ch <= 0xd7ff && ch != 0x7f) || ch == 9 || ch == 10 || ch == 13 || (0xe000 <= ch && ch <= 0xfffd) || (0xffff < ch && ch <= 0x10ffff))
buf[j++] = ch;
#else
if ((0x20 <= ch && ch <= 0xd7ff && ch != 0x7f) || ch == 9 || ch == 10 || ch == 13 || (0xd000 <= ch && ch <= 0xfffd)) {
@ -349,7 +351,7 @@ clean_xml_chars(PyObject *self, PyObject *text) {
if (ch <= 0xdbff && i + 1 < PyUnicode_GET_SIZE(text) && 0xdc00 <= PyUnicode_AS_UNICODE(text)[i + 1] && PyUnicode_AS_UNICODE(text)[i+1] <= 0xdfff) {
buf[j++] = ch; buf[j++] = PyUnicode_AS_UNICODE(text)[++i];
}
} else
} else
buf[j++] = ch;
}
#endif
@ -490,6 +492,48 @@ speedup_iso_8601(PyObject *self, PyObject *args) {
return Py_BuildValue("NOi", PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, usecond), (tzhour == 1000) ? Py_False : Py_True, tzsign*60*(tzhour*60 + tzminute));
}
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#define FREEBSD_SET_NAME
#endif
#if defined(__APPLE__)
// I cant figure out how to get pthread.h to include this definition on macOS. MACOSX_DEPLOYMENT_TARGET does not work.
extern int pthread_setname_np(const char *name);
#elif defined(FREEBSD_SET_NAME)
// Function has a different name on FreeBSD
void pthread_set_name_np(pthread_t tid, const char *name);
#else
// Need _GNU_SOURCE for pthread_setname_np on linux and that causes other issues on systems with old glibc
extern int pthread_setname_np(pthread_t, const char *name);
#endif
static PyObject*
set_thread_name(PyObject *self, PyObject *args) {
(void)(self); (void)(args);
#if defined(_MSC_VER)
PyErr_SetString(PyExc_OSError, "Setting thread names not supported on windows");
return NULL;
#else
char *name;
int ret;
if (!PyArg_ParseTuple(args, "s", &name)) return NULL;
while (1) {
errno = 0;
#if defined(__APPLE__)
ret = pthread_setname_np(name);
#elif defined(FREEBSD_SET_NAME)
pthread_set_name_np(pthread_self(), name);
ret = 0;
#else
ret = pthread_setname_np(pthread_self(), name);
#endif
if (ret != 0 && (errno == EINTR || errno == EAGAIN)) continue;
break;
}
if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
Py_RETURN_NONE;
#endif
}
static PyMethodDef speedup_methods[] = {
{"parse_date", speedup_parse_date, METH_VARARGS,
@ -534,6 +578,10 @@ static PyMethodDef speedup_methods[] = {
"clean_xml_chars(unicode_object)\n\nRemove codepoints in unicode_object that are not allowed in XML"
},
{"set_thread_name", set_thread_name, METH_VARARGS,
"set_thread_name(name)\n\nWrapper for pthread_setname_np"
},
{NULL, NULL, 0, NULL}
};