Skip to content

Commit

Permalink
Updates for free-threading support
Browse files Browse the repository at this point in the history
These are updates based on the audit of a number
of source files in pyobjc-core. Slowly getting
closer to the parts of the implementation that
need more design work w.r.t. locking.

Issue #608
  • Loading branch information
ronaldoussoren committed Aug 7, 2024
1 parent 16eee0c commit 3f4c709
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 97 deletions.
3 changes: 1 addition & 2 deletions pyobjc-core/Modules/objc/block_support.m
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@
struct block_literal* src = (struct block_literal*)_src;

PyObjC_BEGIN_WITH_GIL
dst->invoke_cleanup = src->invoke_cleanup;
Py_XINCREF(dst->invoke_cleanup);
dst->invoke_cleanup = Py_XNewRef(src->invoke_cleanup);

PyObjC_END_WITH_GIL
}
Expand Down
27 changes: 26 additions & 1 deletion pyobjc-core/Modules/objc/closure_pool.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* alive forever and we therefore don't have to return storage to the OS.
*
* These functions are only used when deploying to macOS 10.14 or earlier.
*
*/
#include "pyobjc.h"
#include "closure_pool.h"
Expand All @@ -18,6 +19,15 @@
struct freelist* _Nullable next;
} freelist;

#if PY_VERSION_HEX >= 0x030d0000
/*
* Mutex protecting *closure_freelist*. There is no need for
* minimizing the amount of code protected by the lock, allocating
* of new closures is not anywhere close to being on a fast path.
*/
static PyMutex freelist_mutex = {0};
#endif

static freelist* closure_freelist = NULL;

#ifdef MAP_JIT
Expand All @@ -29,7 +39,7 @@
static int
use_map_jit(void)
{
static int cached_result = -1;
static _Atomic int cached_result = -1;

if (cached_result == -1) {
char buf[256];
Expand Down Expand Up @@ -93,23 +103,38 @@
return NULL;
}
PyObjC_Assert(codeloc, NULL);
#if PY_VERSION_HEX >= 0x030d0000
PyMutex_Lock(&freelist_mutex);
#endif
if (closure_freelist == NULL) {
closure_freelist = allocate_block();
if (closure_freelist == NULL) {
#if PY_VERSION_HEX >= 0x030d0000
PyMutex_Unlock(&freelist_mutex);
#endif
return NULL;
}
}
ffi_closure* result = (ffi_closure*)closure_freelist;
closure_freelist = closure_freelist->next;
*codeloc = (void*)result;
#if PY_VERSION_HEX >= 0x030d0000
PyMutex_Unlock(&freelist_mutex);
#endif
return result;
}

int
PyObjC_ffi_closure_free(ffi_closure* cl)
{
#if PY_VERSION_HEX >= 0x030d0000
PyMutex_Lock(&freelist_mutex);
#endif
((freelist*)cl)->next = closure_freelist;
closure_freelist = (freelist*)cl;
#if PY_VERSION_HEX >= 0x030d0000
PyMutex_Unlock(&freelist_mutex);
#endif
return 0;
}

Expand Down
178 changes: 110 additions & 68 deletions pyobjc-core/Modules/objc/file_wrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

/* A basic wrapper for C's "FILE*"
* that implements a usable API.
*
* NOTE: The locking using critical section is not very fine grained,
* but that shouldn't be a problem given how little FILE* objects
* are used in Objective-C APIs.
*/

static PyObject* FILE_Type;
Expand Down Expand Up @@ -56,6 +60,8 @@
{
struct file_object* self = (struct file_object*)_self;

Py_BEGIN_CRITICAL_SECTION(_self);

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Closing closed file");
return NULL;
Expand All @@ -67,75 +73,97 @@

self->fp = NULL;

Py_END_CRITICAL_SECTION();

Py_INCREF(Py_None);
return Py_None;
}

static PyObject* _Nullable file_flush(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
PyObject* retval;
int result;

Py_BEGIN_CRITICAL_SECTION(_self);

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}

result = fflush(self->fp);
if (result != 0) {
return PyErr_SetFromErrno(PyExc_OSError);
retval = NULL;
} else {
result = fflush(self->fp);
if (result != 0) {
retval = PyErr_SetFromErrno(PyExc_OSError);
} else {
Py_INCREF(Py_None);
retval = Py_None;
}
}

Py_INCREF(Py_None);
return Py_None;
Py_END_CRITICAL_SECTION();
return retval;
}

static PyObject* _Nullable file_errors(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
int result;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
retval = NULL;
} else {
result = ferror(self->fp);
retval = PyBool_FromLong(result);
}

result = ferror(self->fp);
Py_END_CRITICAL_SECTION();

return PyBool_FromLong(result);
return retval;
}

static PyObject* _Nullable file_at_eof(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
int result;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
retval = NULL;
} else {
result = feof(self->fp);
retval = PyBool_FromLong(result);
}
Py_END_CRITICAL_SECTION();

result = feof(self->fp);

return PyBool_FromLong(result);
return retval;
}

static PyObject* _Nullable file_tell(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
long offset;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);
if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}

offset = ftell(self->fp);
if (offset < 0) {
return PyErr_SetFromErrno(PyExc_OSError);
retval = NULL;
} else {
offset = ftell(self->fp);
if (offset < 0) {
retval = PyErr_SetFromErrno(PyExc_OSError);
} else {
retval = PyLong_FromLong(offset);
}
}

return PyLong_FromLong(offset);
Py_END_CRITICAL_SECTION();
return retval;
}

static PyObject* _Nullable file_seek(PyObject* _self, PyObject* args, PyObject* kwds)
Expand All @@ -146,39 +174,46 @@
Py_ssize_t offset;
int whence;
long result;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);
if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}

if (!PyArg_ParseTupleAndKeywords(args, kwds, "ni", keywords, &offset, &whence)) {
return NULL;
}

result = fseek(self->fp, offset, whence);
if (result < 0) {
return PyErr_SetFromErrno(PyExc_OSError);
retval = NULL;
} else if (!PyArg_ParseTupleAndKeywords(args, kwds, "ni", keywords, &offset, &whence)) {
retval = NULL;
} else {
result = fseek(self->fp, offset, whence);
if (result < 0) {
retval = PyErr_SetFromErrno(PyExc_OSError);
} else {
Py_INCREF(Py_None);
retval = Py_None;
}
}
Py_END_CRITICAL_SECTION();

Py_INCREF(Py_None);
return Py_None;
return retval;
}

static PyObject* _Nullable file_fileno(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
int fd;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);
if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}

fd = fileno(self->fp);
/* According to the manpage this function cannot fail */
retval = NULL;
} else {
fd = fileno(self->fp);
/* According to the manpage this function cannot fail */

return PyLong_FromLong(fd);
retval = PyLong_FromLong(fd);
}
Py_END_CRITICAL_SECTION();
return retval;
}

static PyObject* _Nullable file_write(PyObject* _self, PyObject* args, PyObject* kwds)
Expand All @@ -189,37 +224,45 @@
void* buffer;
Py_ssize_t buffer_size;
size_t result;

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}
PyObject* retval;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "y#", keywords, &buffer, &buffer_size)) {
return NULL;
}

result = fwrite(buffer, 1, buffer_size, self->fp);
return Py_BuildValue("k", (unsigned long)result);
Py_BEGIN_CRITICAL_SECTION(_self);
if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
retval = NULL;
} else {
result = fwrite(buffer, 1, buffer_size, self->fp);
retval = PyLong_FromSize_t(result);
}
Py_END_CRITICAL_SECTION();
return retval;
}

static PyObject* _Nullable file_readline(PyObject* _self)
{
struct file_object* self = (struct file_object*)_self;
char buffer[2048];
char* result;
PyObject* retval;

Py_BEGIN_CRITICAL_SECTION(_self);
if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}

result = fgets(buffer, 2048, self->fp);
if (result == NULL) {
return PyBytes_FromStringAndSize("", 0);
} else {
return PyBytes_FromString(result);
result = fgets(buffer, 2048, self->fp);
if (result == NULL) {
retval = PyBytes_FromStringAndSize("", 0);
} else {
retval = PyBytes_FromString(result);
}
}
Py_END_CRITICAL_SECTION();
return retval;
}

static PyObject* _Nullable file_read(PyObject* _self, PyObject* args, PyObject* kwds)
Expand All @@ -231,23 +274,22 @@
Py_ssize_t buffer_size;
size_t result;

Py_BEGIN_CRITICAL_SECTION(_self);

if (self->fp == NULL) {
PyErr_SetString(PyExc_ValueError, "Using closed file");
return NULL;
}
buffer = NULL;
} else if (!PyArg_ParseTupleAndKeywords(args, kwds, "n", keywords, &buffer_size)) {
buffer = NULL;
} else {
buffer = PyBytes_FromStringAndSize(NULL, buffer_size);
if (buffer != NULL) {
result = fread(PyBytes_AsString(buffer), 1, buffer_size, self->fp);

if (!PyArg_ParseTupleAndKeywords(args, kwds, "n", keywords, &buffer_size)) {
return NULL;
_PyBytes_Resize(&buffer, result);
}
}

buffer = PyBytes_FromStringAndSize(NULL, buffer_size);
if (buffer == NULL) {
return NULL;
}

result = fread(PyBytes_AsString(buffer), 1, buffer_size, self->fp);

_PyBytes_Resize(&buffer, result);
Py_END_CRITICAL_SECTION();
return buffer;
}

Expand Down
Loading

0 comments on commit 3f4c709

Please sign in to comment.