-
Notifications
You must be signed in to change notification settings - Fork 69
Fixes SIGSEGV and improve thread safety of journal.Reader class #144
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
base: main
Are you sure you want to change the base?
Changes from all commits
245784c
7fa91c8
7c99a39
646b3b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,27 +14,28 @@ permissions: | |
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-20.04 | ||
runs-on: ubuntu-latest | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ matrix.python }}-${{ github.ref }} | ||
cancel-in-progress: true | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
python: [ | ||
"3.7", | ||
"3.8", | ||
"3.9", | ||
"3.10", | ||
"3.11.0-rc.1", | ||
"3.11", | ||
"3.12", | ||
"3.13" | ||
] | ||
name: Python ${{ matrix.python }} | ||
steps: | ||
- name: Repository checkout | ||
uses: actions/checkout@v2 | ||
uses: actions/checkout@v4 | ||
|
||
- name: Configure Python ${{ matrix.python }} | ||
uses: actions/setup-python@v2 | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: ${{ matrix.python }} | ||
architecture: x64 | ||
|
@@ -43,6 +44,9 @@ jobs: | |
run: | | ||
sudo apt -y update | ||
sudo apt -y install gcc libsystemd-dev | ||
if dpkg --compare-versions "${{ matrix.python }}" ge 3.12; then | ||
python -m pip install setuptools || echo "can't install setuptools" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might make sense to always install setuptools, so that all versions are tested with the same? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dunno... IMHO, only 3.12+ missing it as package, but I am unsure the attempt to install with pip for previous versions would be always successful (and not fail due to conflict with preinstalled package). |
||
fi | ||
python -m pip install pytest sphinx | ||
|
||
- name: Build (Python ${{ matrix.python }}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,8 @@ | |
typedef struct { | ||
PyObject_HEAD | ||
sd_journal *j; | ||
unsigned closed; | ||
unsigned ref_count; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the future, please explain the reasoning behind a patch in the commit message. For something like thread safety, this is particularly important. Without that, the reader is left to guess. I don't understand how this is supposed to help. This is a python object, and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, you did provide an explanation, but in the pull request ticket. I was looking at the patches and missed that. Sorry. (The explanation still belongs in the commit message, not on a pull request.) I'll reply further in #143, because you make some good points there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, matter of taste either (many repo-owners like to hold the commit messages as short as possible)... |
||
} Reader; | ||
static PyTypeObject ReaderType; | ||
|
||
|
@@ -89,6 +91,31 @@ static PyStructSequence_Desc Monotonic_desc = { | |
2, | ||
}; | ||
|
||
static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { | ||
Reader *self = (Reader *)PyType_GenericNew(type, args, kwds); | ||
self->j = NULL; | ||
self->closed = 0; | ||
self->ref_count = 1; /* initial reference */ | ||
return (PyObject *)self; | ||
} | ||
|
||
static inline void decr_ref_count(Reader *self) { | ||
if (!self->ref_count) return; | ||
if (!--self->ref_count && self->j) { | ||
sd_journal_close(self->j); | ||
self->j = NULL; | ||
} | ||
} | ||
|
||
#define INCR_REF_BEGIN_ALLOW_THREADS(self) \ | ||
self->ref_count++; \ | ||
Py_BEGIN_ALLOW_THREADS | ||
#define DECR_REF_END_ALLOW_THREADS(self) \ | ||
Py_END_ALLOW_THREADS \ | ||
decr_ref_count(self); | ||
|
||
|
||
|
||
/** | ||
* Convert a str or bytes object into a C-string path. | ||
* Returns NULL on error. | ||
|
@@ -220,7 +247,9 @@ static int intlist_converter(PyObject* obj, int **_result, size_t *_len) { | |
} | ||
|
||
static void Reader_dealloc(Reader* self) { | ||
sd_journal_close(self->j); | ||
if (self->j) { | ||
sd_journal_close(self->j); | ||
} | ||
Py_TYPE(self)->tp_free((PyObject*)self); | ||
} | ||
|
||
|
@@ -271,9 +300,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { | |
return -1; | ||
|
||
#if HAVE_JOURNAL_OPEN_DIRECTORY_FD | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open_directory_fd(&self->j, (int) fd, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
#else | ||
r = -ENOSYS; | ||
#endif | ||
|
@@ -285,9 +314,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { | |
if (!path) | ||
return -1; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open_directory(&self->j, path, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
} | ||
} else if (_files) { | ||
_cleanup_Py_DECREF_ PyObject *item0 = NULL; | ||
|
@@ -300,9 +329,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { | |
return -1; | ||
|
||
#if HAVE_JOURNAL_OPEN_FILES | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open_files(&self->j, (const char**) files, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
#else | ||
r = -ENOSYS; | ||
#endif | ||
|
@@ -314,9 +343,9 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { | |
return -1; | ||
|
||
#if HAVE_JOURNAL_OPEN_DIRECTORY_FD | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open_files_fd(&self->j, fds, n_fds, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
#else | ||
r = -ENOSYS; | ||
#endif | ||
|
@@ -329,16 +358,16 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { | |
if (!namespace) | ||
return -1; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open_namespace(&self->j, namespace, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
#else | ||
r = -ENOSYS; | ||
#endif | ||
} else { | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_open(&self->j, flags); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
} | ||
|
||
return set_error(r, NULL, "Opening the journal failed"); | ||
|
@@ -438,8 +467,10 @@ static PyObject* Reader_close(Reader *self, PyObject *args) { | |
assert(self); | ||
assert(!args); | ||
|
||
sd_journal_close(self->j); | ||
self->j = NULL; | ||
if (!self->closed) { | ||
self->closed = 1; | ||
decr_ref_count(self); /* decrement initial reference (without incr) */ | ||
} | ||
Py_RETURN_NONE; | ||
} | ||
|
||
|
@@ -501,7 +532,7 @@ static PyObject* Reader_next(Reader *self, PyObject *args) { | |
return NULL; | ||
} | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
if (skip == 1LL) | ||
r = sd_journal_next(self->j); | ||
else if (skip == -1LL) | ||
|
@@ -512,7 +543,7 @@ static PyObject* Reader_next(Reader *self, PyObject *args) { | |
r = sd_journal_previous_skip(self->j, -skip); | ||
else | ||
assert(!"should be here"); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -782,9 +813,9 @@ PyDoc_STRVAR(Reader_seek_head__doc__, | |
"See :manpage:`sd_journal_seek_head(3)`."); | ||
static PyObject* Reader_seek_head(Reader *self, PyObject *args) { | ||
int r; | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_seek_head(self->j); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -800,9 +831,9 @@ PyDoc_STRVAR(Reader_seek_tail__doc__, | |
static PyObject* Reader_seek_tail(Reader *self, PyObject *args) { | ||
int r; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_seek_tail(self->j); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -820,9 +851,9 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { | |
if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) | ||
return NULL; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_seek_realtime_usec(self->j, timestamp); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -850,17 +881,17 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { | |
if (set_error(r, NULL, "Invalid bootid") < 0) | ||
return NULL; | ||
} else { | ||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_id128_get_boot(&id); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
} | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_seek_monotonic_usec(self->j, id, timestamp); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -924,9 +955,9 @@ static PyObject* Reader_process(Reader *self, PyObject *args) { | |
|
||
assert(!args); | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_process(self->j); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
||
|
@@ -950,9 +981,9 @@ static PyObject* Reader_wait(Reader *self, PyObject *args) { | |
if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) | ||
return NULL; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_wait(self->j, timeout); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, NULL) < 0) | ||
return NULL; | ||
|
@@ -970,9 +1001,9 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) { | |
if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor)) | ||
return NULL; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_seek_cursor(self->j, cursor); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, "Invalid cursor") < 0) | ||
return NULL; | ||
|
@@ -1035,9 +1066,9 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) { | |
if (!PyArg_ParseTuple(args, "s:query_unique", &query)) | ||
return NULL; | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_query_unique(self->j, query); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (set_error(r, NULL, "Invalid field name") < 0) | ||
return NULL; | ||
|
@@ -1172,9 +1203,9 @@ static PyObject* Reader_get_catalog(Reader *self, PyObject *args) { | |
assert(self); | ||
assert(!args); | ||
|
||
Py_BEGIN_ALLOW_THREADS | ||
INCR_REF_BEGIN_ALLOW_THREADS(self) | ||
r = sd_journal_get_catalog(self->j, &msg); | ||
Py_END_ALLOW_THREADS | ||
DECR_REF_END_ALLOW_THREADS(self) | ||
|
||
if (r == -ENOENT) { | ||
const void* mid; | ||
|
@@ -1264,7 +1295,7 @@ static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closur | |
PyDoc_STRVAR(closed__doc__, | ||
"True iff journal is closed"); | ||
static PyObject* Reader_get_closed(Reader *self, void *closure) { | ||
return PyBool_FromLong(!self->j); | ||
return PyBool_FromLong(self->closed || !self->j); | ||
} | ||
|
||
static PyGetSetDef Reader_getsetters[] = { | ||
|
@@ -1330,7 +1361,7 @@ static PyTypeObject ReaderType = { | |
.tp_methods = Reader_methods, | ||
.tp_getset = Reader_getsetters, | ||
.tp_init = (initproc) Reader_init, | ||
.tp_new = PyType_GenericNew, | ||
.tp_new = Reader_new, | ||
}; | ||
|
||
static PyMethodDef methods[] = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should probably be dropped as well, since it is EOL or in other words: with 3.7 dropped I see no reason not to drop 3.8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dropped 3.7 only because of switch to ubuntu-latest, which doesn't have it.
I don't see any reason to drop 3.8. at least as long as it is compatible and running yet.
Anyway has nothing to do with the PR as is, I made the changes just in order to repair the GHA flow.