Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions Modules/_winapi.c
Original file line numberDiff line numberDiff line change
Expand Up@@ -112,6 +112,20 @@ typedef struct{
Py_buffer write_buffer;
} OverlappedObject;

/*
Note: tp_clear (overlapped_clear) is not implemented because it
requires cancelling the IO operation if it's pending and the cancellation is
quite complex and can fail (see: overlapped_dealloc).
*/
static int
overlapped_traverse(OverlappedObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->read_buffer);
Py_VISIT(self->write_buffer.obj);
Py_VISIT(Py_TYPE(self));
return 0;
}

static void
overlapped_dealloc(OverlappedObject *self)
{
Expand DownExpand Up@@ -150,6 +164,7 @@ overlapped_dealloc(OverlappedObject *self)

CloseHandle(self->overlapped.hEvent);
SetLastError(err);
PyObject_GC_UnTrack(self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to start by untracking the GC, at the start to the dealloc function.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially did that, but a concern was brought up here which I think makes sense #26381 (comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If tp_dealloc returns without clearing the object, IMO it's better to remove it from the GC. It's better to hide this badly broken Python object. I suggest to start by untracking and explain that it's a deliberate choice to untrack even if we hit the worst case scenario of "return" below.

Note: IMO this code should be removed, Windows XP is no longer supported, but it should be done in a separated change.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I just introduced a regression with this, seems like there's a race condition with the GC and the IO operation somewhere.

> python_d.exe -m test test_httplib -m test_local_bad_hostname ...\cpython\Modules\gcmodule.c:446: update_refs: Assertion "gc_get_refs(gc) != 0" failed Enable tracemalloc to get the memory block allocation traceback object address : 000002DADE2A99D0 object refcount : 0 object type : 000002DADDF72F60 object type name: _winapi.Overlapped object repr : <refcnt 0 at 000002DADE2A99D0> Fatal Python error: _PyObject_AssertFailed: _PyObject_AssertFailed Python runtime state: initialized 

It seems that placing the PyObject_GC_UnTrack at the top actually fixes it...

if (self->write_buffer.obj)
PyBuffer_Release(&self->write_buffer);
Py_CLEAR(self->read_buffer);
Expand DownExpand Up@@ -321,6 +336,7 @@ static PyMemberDef overlapped_members[] ={
};

static PyType_Slot winapi_overlapped_type_slots[] ={
{Py_tp_traverse, overlapped_traverse},
{Py_tp_dealloc, overlapped_dealloc},
{Py_tp_doc, "OVERLAPPED structure wrapper"},
{Py_tp_methods, overlapped_methods},
Expand All@@ -331,15 +347,16 @@ static PyType_Slot winapi_overlapped_type_slots[] ={
static PyType_Spec winapi_overlapped_type_spec ={
.name = "_winapi.Overlapped",
.basicsize = sizeof(OverlappedObject),
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
Py_TPFLAGS_HAVE_GC),
.slots = winapi_overlapped_type_slots,
};

static OverlappedObject *
new_overlapped(PyObject *module, HANDLE handle)
{
WinApiState *st = winapi_get_state(module);
OverlappedObject *self = PyObject_New(OverlappedObject, st->overlapped_type);
OverlappedObject *self = PyObject_GC_New(OverlappedObject, st->overlapped_type);
if (!self)
return NULL;

Expand All@@ -351,6 +368,8 @@ new_overlapped(PyObject *module, HANDLE handle)
memset(&self->write_buffer, 0, sizeof(Py_buffer));
/* Manual reset, initially non-signalled */
self->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

PyObject_GC_Track(self);
return self;
}

Expand DownExpand Up@@ -2043,12 +2062,37 @@ static PyModuleDef_Slot winapi_slots[] ={
{0, NULL}
};

static int
winapi_traverse(PyObject *module, visitproc visit, void *arg)
{
WinApiState *st = winapi_get_state(module);
Py_VISIT(st->overlapped_type);
return 0;
}

static int
winapi_clear(PyObject *module)
{
WinApiState *st = winapi_get_state(module);
Py_CLEAR(st->overlapped_type);
return 0;
}

static void
winapi_free(void *module)
{
winapi_clear((PyObject *)module);
}

static struct PyModuleDef winapi_module ={
PyModuleDef_HEAD_INIT,
.m_name = "_winapi",
.m_size = sizeof(WinApiState),
.m_methods = winapi_functions,
.m_slots = winapi_slots,
.m_traverse = winapi_traverse,
.m_clear = winapi_clear,
.m_free = winapi_free,
};

PyMODINIT_FUNC
Expand Down