diff --git a/pyproject.toml b/pyproject.toml index fefefa8..ed46010 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Operating System :: POSIX :: Linux", "Programming Language :: C", "Programming Language :: Python :: 3", + "Programming Language :: Python :: Free Threading :: 2 - Beta", "Topic :: System :: Archiving :: Packaging", ] diff --git a/src/python/client.c b/src/python/client.c index 7f97f97..ca4e18f 100644 --- a/src/python/client.c +++ b/src/python/client.c @@ -24,6 +24,7 @@ typedef struct PyObject_HEAD gchar * name; assuan_context_t ctx; + GRWLock lock; } ClientObject; static PyObject * @@ -39,6 +40,7 @@ client_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (self) { self->ctx = NULL; self->name = NULL; + g_rw_lock_init(&self->lock); } return (PyObject *)self; } @@ -57,16 +59,21 @@ client_init(ClientObject *self, PyObject *args, PyObject *kwds) return -1; } + g_rw_lock_writer_lock(&self->lock); + if (NULL != self->name) { g_free(self->name); } self->name = g_strdup(name); if (NULL == self->name) { + g_rw_lock_writer_unlock(&self->lock); PyErr_NoMemory(); return -1; } + g_rw_lock_writer_unlock(&self->lock); + return 0; } static void @@ -74,6 +81,7 @@ client_dealloc(ClientObject *self) { Py_XDECREF(client_disconnect(self, NULL)); + g_rw_lock_clear(&self->lock); if (NULL != self->name) { g_free(self->name); } @@ -84,8 +92,14 @@ client_dealloc(ClientObject *self) static PyObject * client_repr(ClientObject *self) { - return PyUnicode_FromFormat( + g_rw_lock_reader_lock(&self->lock); + + PyObject *res = PyUnicode_FromFormat( "", self->name); + + g_rw_lock_reader_unlock(&self->lock); + + return res; } static PyObject * @@ -93,7 +107,16 @@ execute_transaction(ClientObject *self, const char * cmd) { gpg_error_t rc; + Py_BEGIN_ALLOW_THREADS + + g_rw_lock_writer_lock(&self->lock); + rc = assuan_transact(self->ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL); + + g_rw_lock_writer_unlock(&self->lock); + + Py_END_ALLOW_THREADS + if (rc) { PyErr_Format(PyExc_RuntimeError, "Transaction failed: %s", gpg_strerror(rc)); return NULL; @@ -198,6 +221,8 @@ client_connect(ClientObject *self, PyObject *args) gpg_error_t rc; + g_rw_lock_writer_lock(&self->lock); + gchar *cwd = g_path_is_absolute(self->name) ? NULL : g_get_current_dir(); gchar *sockpath = g_strconcat( cwd ? cwd : "", @@ -208,12 +233,14 @@ client_connect(ClientObject *self, PyObject *args) NULL); g_free(cwd); if (NULL == sockpath) { + g_rw_lock_writer_unlock(&self->lock); return PyErr_NoMemory(); } assuan_release(self->ctx); rc = assuan_new(&self->ctx); if (rc) { + g_rw_lock_writer_unlock(&self->lock); PyErr_Format(PyExc_RuntimeError, "Failed to initialize Assuan context: %s", gpg_strerror(rc)); g_free(sockpath); return NULL; @@ -222,10 +249,14 @@ client_connect(ClientObject *self, PyObject *args) rc = assuan_socket_connect(self->ctx, sockpath, ASSUAN_INVALID_PID, 0); g_free(sockpath); if (rc) { + assuan_release(self->ctx); + g_rw_lock_writer_unlock(&self->lock); PyErr_Format(PyExc_RuntimeError, "Failed to connect to server: %s", gpg_strerror(rc)); return NULL; } + g_rw_lock_writer_unlock(&self->lock); + Py_RETURN_NONE; } @@ -234,9 +265,13 @@ client_disconnect(ClientObject *self, PyObject *args) { (void)args; + g_rw_lock_writer_lock(&self->lock); + assuan_release(self->ctx); self->ctx = NULL; + g_rw_lock_writer_unlock(&self->lock); + Py_RETURN_NONE; } @@ -349,7 +384,13 @@ client_get_name(ClientObject *self, void *closure) { (void)closure; - return PyUnicode_FromString(self->name); + g_rw_lock_reader_lock(&self->lock); + + PyObject *res = PyUnicode_FromString(self->name); + + g_rw_lock_reader_unlock(&self->lock); + + return res; } static struct PyMethodDef client_methods[] = { diff --git a/src/python/init.c b/src/python/init.c index 936e0c9..2c05148 100644 --- a/src/python/init.c +++ b/src/python/init.c @@ -71,5 +71,9 @@ PyInit_createrepo_agent(void) PyModule_AddIntConstant(m, "EXIT_USAGE", CRA_EXIT_USAGE); PyModule_AddIntConstant(m, "EXIT_IN_USE", CRA_EXIT_IN_USE); +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + return m; } diff --git a/src/python/server.c b/src/python/server.c index c3c48db..1c31eea 100644 --- a/src/python/server.c +++ b/src/python/server.c @@ -30,6 +30,7 @@ typedef struct gchar * name; volatile sig_atomic_t sentinel; GThread * thread; + GRWLock lock; } ServerObject; static PyObject * @@ -37,7 +38,13 @@ server_shutdown_thread(ServerObject *self, PyObject *args); static void * server_thread(ServerObject *self) { - command_handler(self->fd, self->name, &self->sentinel); + g_rw_lock_reader_lock(&self->lock); + + if (0 == self->sentinel && ASSUAN_INVALID_FD != self->fd) { + command_handler(self->fd, self->name, &self->sentinel); + } + + g_rw_lock_reader_unlock(&self->lock); return NULL; } @@ -54,6 +61,7 @@ server_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->name = NULL; self->sentinel = 0; self->thread = NULL; + g_rw_lock_init(&self->lock); } return (PyObject *)self; } @@ -72,16 +80,31 @@ server_init(ServerObject *self, PyObject *args, PyObject *kwds) return -1; } + g_rw_lock_reader_lock(&self->lock); + + if (ASSUAN_INVALID_FD != self->fd) { + PyErr_SetString(PyExc_RuntimeError, "Server is already active"); + g_rw_lock_reader_unlock(&self->lock); + return -1; + } + + g_rw_lock_reader_unlock(&self->lock); + + g_rw_lock_writer_lock(&self->lock); + if (NULL != self->name) { g_free(self->name); } self->name = g_strdup(name); if (NULL == self->name) { + g_rw_lock_writer_unlock(&self->lock); PyErr_NoMemory(); return -1; } + g_rw_lock_writer_unlock(&self->lock); + return 0; } @@ -90,6 +113,7 @@ server_dealloc(ServerObject *self) { Py_XDECREF(server_shutdown_thread(self, NULL)); + g_rw_lock_clear(&self->lock); if (NULL != self->name) { g_free(self->name); } @@ -100,8 +124,14 @@ server_dealloc(ServerObject *self) static PyObject * server_repr(ServerObject *self) { - return PyUnicode_FromFormat( + g_rw_lock_reader_lock(&self->lock); + + PyObject *res = PyUnicode_FromFormat( "", self->name); + + g_rw_lock_reader_unlock(&self->lock); + + return res; } static PyObject * @@ -109,7 +139,30 @@ server_shutdown_thread(ServerObject *self, PyObject *args) { (void)args; + g_rw_lock_reader_lock(&self->lock); + + if (ASSUAN_INVALID_FD == self->fd && NULL == self->thread) { + g_rw_lock_reader_unlock(&self->lock); + Py_RETURN_NONE; + } + self->sentinel = 1; + + if (ASSUAN_INVALID_FD != self->fd) { + shutdown(self->fd, SHUT_RD); + } + + Py_BEGIN_ALLOW_THREADS + + if (NULL != self->thread) { + g_thread_ref(self->thread); + g_thread_join(self->thread); + } + + g_rw_lock_reader_unlock(&self->lock); + + g_rw_lock_writer_lock(&self->lock); + if (ASSUAN_INVALID_FD != self->fd) { shutdown(self->fd, SHUT_RD); self->fd = ASSUAN_INVALID_FD; @@ -122,6 +175,10 @@ server_shutdown_thread(ServerObject *self, PyObject *args) self->sentinel = 0; + g_rw_lock_writer_unlock(&self->lock); + + Py_END_ALLOW_THREADS + Py_RETURN_NONE; } @@ -130,8 +187,11 @@ server_start_thread(ServerObject *self, PyObject *args) { (void)args; + g_rw_lock_reader_lock(&self->lock); + if (ASSUAN_INVALID_FD != self->fd) { PyErr_SetString(PyExc_RuntimeError, "Server is already active"); + g_rw_lock_reader_unlock(&self->lock); return NULL; } @@ -143,11 +203,16 @@ server_start_thread(ServerObject *self, PyObject *args) g_str_has_suffix(self->name, "/") ? "" : "/", CRA_SOCK_NAME, NULL); + g_rw_lock_reader_unlock(&self->lock); g_free(cwd); if (NULL == sockpath) { return PyErr_NoMemory(); } + Py_BEGIN_ALLOW_THREADS + + g_rw_lock_writer_lock(&self->lock); + self->fd = create_server_socket(sockpath); if (self->fd == ASSUAN_INVALID_FD && errno == EADDRINUSE) { gpg_error_t res = try_server(sockpath); @@ -159,7 +224,11 @@ server_start_thread(ServerObject *self, PyObject *args) errno = EADDRINUSE; } } + + Py_END_ALLOW_THREADS + if (ASSUAN_INVALID_FD == self->fd) { + g_rw_lock_writer_unlock(&self->lock); PyErr_SetFromErrnoWithFilename(PyExc_OSError, sockpath); g_free(sockpath); return NULL; @@ -171,10 +240,13 @@ server_start_thread(ServerObject *self, PyObject *args) if (!self->thread) { assuan_sock_close(self->fd); self->fd = ASSUAN_INVALID_FD; + g_rw_lock_writer_unlock(&self->lock); PyErr_SetString(PyExc_RuntimeError, "Failed to start thread"); return NULL; } + g_rw_lock_writer_unlock(&self->lock); + Py_RETURN_NONE; } @@ -199,7 +271,13 @@ server_get_name(ServerObject *self, void *closure) { (void)closure; - return PyUnicode_FromString(self->name); + g_rw_lock_reader_lock(&self->lock); + + PyObject *res = PyUnicode_FromString(self->name); + + g_rw_lock_reader_unlock(&self->lock); + + return res; } static struct PyMethodDef server_methods[] = {