Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ffa2c5

Browse files
committed
Issue #23293, asyncio: Rewrite IocpProactor.connect_pipe()
Add _overlapped.ConnectPipe() which tries to connect to the pipe for asynchronous I/O (overlapped): call CreateFile() in a loop until it doesn't fail with ERROR_PIPE_BUSY. Use an increasing delay between 1 ms and 100 ms. Remove Overlapped.WaitNamedPipeAndConnect() which is no more used.
1 parent 752aba7 commit 7ffa2c5

2 files changed

Lines changed: 48 additions & 110 deletions

File tree

Lib/asyncio/windows_events.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
ERROR_CONNECTION_REFUSED = 1225
3030
ERROR_CONNECTION_ABORTED = 1236
3131

32+
# Initial delay in seconds for connect_pipe() before retrying to connect
33+
CONNECT_PIPE_INIT_DELAY = 0.001
34+
35+
# Maximum delay in seconds for connect_pipe() before retrying to connect
36+
CONNECT_PIPE_MAX_DELAY = 0.100
37+
3238

3339
class _OverlappedFuture(futures.Future):
3440
"""Subclass of Future which represents an overlapped operation.
@@ -495,25 +501,28 @@ def finish_accept_pipe(trans, key, ov):
495501
return self._register(ov, pipe, finish_accept_pipe,
496502
register=False)
497503

498-
def connect_pipe(self, address):
499-
ov = _overlapped.Overlapped(NULL)
500-
ov.WaitNamedPipeAndConnect(address, self._iocp, ov.address)
501-
502-
def finish_connect_pipe(err, handle, ov):
503-
# err, handle were arguments passed to PostQueuedCompletionStatus()
504-
# in a function run in a thread pool.
505-
if err == _overlapped.ERROR_SEM_TIMEOUT:
506-
# Connection did not succeed within time limit.
507-
msg = _overlapped.FormatMessage(err)
508-
raise ConnectionRefusedError(0, msg, None, err)
509-
elif err != 0:
510-
msg = _overlapped.FormatMessage(err)
511-
raise OSError(0, msg, None, err)
504+
def _connect_pipe(self, fut, address, delay):
505+
# Unfortunately there is no way to do an overlapped connect to a pipe.
506+
# Call CreateFile() in a loop until it doesn't fail with
507+
# ERROR_PIPE_BUSY
508+
try:
509+
handle = _overlapped.ConnectPipe(address)
510+
except OSError as exc:
511+
if exc.winerror == _overlapped.ERROR_PIPE_BUSY:
512+
# Polling: retry later
513+
delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
514+
self._loop.call_later(delay,
515+
self._connect_pipe, fut, address, delay)
512516
else:
513-
return windows_utils.PipeHandle(handle)
517+
fut.set_exception(exc)
518+
else:
519+
pipe = windows_utils.PipeHandle(handle)
520+
fut.set_result(pipe)
514521

515-
return self._register(ov, None, finish_connect_pipe,
516-
wait_for_post=True)
522+
def connect_pipe(self, address):
523+
fut = futures.Future(loop=self._loop)
524+
self._connect_pipe(fut, address, CONNECT_PIPE_INIT_DELAY)
525+
return fut
517526

518527
def wait_for_handle(self, handle, timeout=None):
519528
"""Wait for a handle.

Modules/overlapped.c

Lines changed: 22 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@ typedef struct {
5252
};
5353
} OverlappedObject;
5454

55-
typedef struct {
56-
OVERLAPPED *Overlapped;
57-
HANDLE IocpHandle;
58-
char Address[1];
59-
} WaitNamedPipeAndConnectContext;
60-
6155
/*
6256
* Map Windows error codes to subclasses of OSError
6357
*/
@@ -1133,99 +1127,33 @@ Overlapped_ConnectNamedPipe(OverlappedObject *self, PyObject *args)
11331127
}
11341128
}
11351129

1136-
/* Unfortunately there is no way to do an overlapped connect to a
1137-
pipe. We instead use WaitNamedPipe() and CreateFile() in a thread
1138-
pool thread. If a connection succeeds within a time limit (10
1139-
seconds) then PostQueuedCompletionStatus() is used to return the
1140-
pipe handle to the completion port. */
1141-
1142-
static DWORD WINAPI
1143-
WaitNamedPipeAndConnectInThread(WaitNamedPipeAndConnectContext *ctx)
1144-
{
1145-
HANDLE PipeHandle = INVALID_HANDLE_VALUE;
1146-
DWORD Start = GetTickCount();
1147-
DWORD Deadline = Start + 10*1000;
1148-
DWORD Error = 0;
1149-
DWORD Timeout;
1150-
BOOL Success;
1151-
1152-
for ( ; ; ) {
1153-
Timeout = Deadline - GetTickCount();
1154-
if ((int)Timeout < 0)
1155-
break;
1156-
Success = WaitNamedPipe(ctx->Address, Timeout);
1157-
Error = Success ? ERROR_SUCCESS : GetLastError();
1158-
switch (Error) {
1159-
case ERROR_SUCCESS:
1160-
PipeHandle = CreateFile(ctx->Address,
1161-
GENERIC_READ | GENERIC_WRITE,
1162-
0, NULL, OPEN_EXISTING,
1163-
FILE_FLAG_OVERLAPPED, NULL);
1164-
if (PipeHandle == INVALID_HANDLE_VALUE)
1165-
continue;
1166-
break;
1167-
case ERROR_SEM_TIMEOUT:
1168-
continue;
1169-
}
1170-
break;
1171-
}
1172-
if (!PostQueuedCompletionStatus(ctx->IocpHandle, Error,
1173-
(ULONG_PTR)PipeHandle, ctx->Overlapped))
1174-
CloseHandle(PipeHandle);
1175-
free(ctx);
1176-
return 0;
1177-
}
1178-
11791130
PyDoc_STRVAR(
1180-
Overlapped_WaitNamedPipeAndConnect_doc,
1181-
"WaitNamedPipeAndConnect(addr, iocp_handle) -> Overlapped[pipe_handle]\n\n"
1182-
"Start overlapped connection to address, notifying iocp_handle when\n"
1183-
"finished");
1131+
ConnectPipe_doc,
1132+
"ConnectPipe(addr) -> pipe_handle\n\n"
1133+
"Connect to the pipe for asynchronous I/O (overlapped).");
11841134

11851135
static PyObject *
1186-
Overlapped_WaitNamedPipeAndConnect(OverlappedObject *self, PyObject *args)
1136+
ConnectPipe(OverlappedObject *self, PyObject *args)
11871137
{
1188-
char *Address;
1189-
Py_ssize_t AddressLength;
1190-
HANDLE IocpHandle;
1191-
OVERLAPPED Overlapped;
1192-
BOOL ret;
1193-
DWORD err;
1194-
WaitNamedPipeAndConnectContext *ctx;
1195-
Py_ssize_t ContextLength;
1138+
PyObject *AddressObj;
1139+
wchar_t *Address;
1140+
HANDLE PipeHandle;
11961141

1197-
if (!PyArg_ParseTuple(args, "s#" F_HANDLE F_POINTER,
1198-
&Address, &AddressLength, &IocpHandle, &Overlapped))
1142+
if (!PyArg_ParseTuple(args, "U", &AddressObj))
11991143
return NULL;
12001144

1201-
if (self->type != TYPE_NONE) {
1202-
PyErr_SetString(PyExc_ValueError, "operation already attempted");
1145+
Address = PyUnicode_AsWideCharString(AddressObj, NULL);
1146+
if (Address == NULL)
12031147
return NULL;
1204-
}
12051148

1206-
ContextLength = (AddressLength +
1207-
offsetof(WaitNamedPipeAndConnectContext, Address));
1208-
ctx = calloc(1, ContextLength + 1);
1209-
if (ctx == NULL)
1210-
return PyErr_NoMemory();
1211-
memcpy(ctx->Address, Address, AddressLength + 1);
1212-
ctx->Overlapped = &self->overlapped;
1213-
ctx->IocpHandle = IocpHandle;
1214-
1215-
self->type = TYPE_WAIT_NAMED_PIPE_AND_CONNECT;
1216-
self->handle = NULL;
1217-
1218-
Py_BEGIN_ALLOW_THREADS
1219-
ret = QueueUserWorkItem(WaitNamedPipeAndConnectInThread, ctx,
1220-
WT_EXECUTELONGFUNCTION);
1221-
Py_END_ALLOW_THREADS
1222-
1223-
mark_as_completed(&self->overlapped);
1224-
1225-
self->error = err = ret ? ERROR_SUCCESS : GetLastError();
1226-
if (!ret)
1227-
return SetFromWindowsErr(err);
1228-
Py_RETURN_NONE;
1149+
PipeHandle = CreateFileW(Address,
1150+
GENERIC_READ | GENERIC_WRITE,
1151+
0, NULL, OPEN_EXISTING,
1152+
FILE_FLAG_OVERLAPPED, NULL);
1153+
PyMem_Free(Address);
1154+
if (PipeHandle == INVALID_HANDLE_VALUE)
1155+
return SetFromWindowsErr(0);
1156+
return Py_BuildValue(F_HANDLE, PipeHandle);
12291157
}
12301158

12311159
static PyObject*
@@ -1262,9 +1190,6 @@ static PyMethodDef Overlapped_methods[] = {
12621190
METH_VARARGS, Overlapped_DisconnectEx_doc},
12631191
{"ConnectNamedPipe", (PyCFunction) Overlapped_ConnectNamedPipe,
12641192
METH_VARARGS, Overlapped_ConnectNamedPipe_doc},
1265-
{"WaitNamedPipeAndConnect",
1266-
(PyCFunction) Overlapped_WaitNamedPipeAndConnect,
1267-
METH_VARARGS, Overlapped_WaitNamedPipeAndConnect_doc},
12681193
{NULL}
12691194
};
12701195

@@ -1350,6 +1275,9 @@ static PyMethodDef overlapped_functions[] = {
13501275
METH_VARARGS, SetEvent_doc},
13511276
{"ResetEvent", overlapped_ResetEvent,
13521277
METH_VARARGS, ResetEvent_doc},
1278+
{"ConnectPipe",
1279+
(PyCFunction) ConnectPipe,
1280+
METH_VARARGS, ConnectPipe_doc},
13531281
{NULL}
13541282
};
13551283

@@ -1394,6 +1322,7 @@ PyInit__overlapped(void)
13941322
WINAPI_CONSTANT(F_DWORD, ERROR_IO_PENDING);
13951323
WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED);
13961324
WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT);
1325+
WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_BUSY);
13971326
WINAPI_CONSTANT(F_DWORD, INFINITE);
13981327
WINAPI_CONSTANT(F_HANDLE, INVALID_HANDLE_VALUE);
13991328
WINAPI_CONSTANT(F_HANDLE, NULL);

0 commit comments

Comments
 (0)