Class: Winloop::Backend
- Inherits:
-
Object
- Object
- Winloop::Backend
- Defined in:
- ext/winloop/winloop.c
Instance Method Summary collapse
-
#cancel(vid) ⇒ Object
Cancel a pending poll.
- #initialize ⇒ Object constructor
-
#poll(io, vevents) ⇒ Object
Arm a one-shot readiness poll on ‘io` for `events`; returns the request id.
- #shutdown ⇒ Object
- #wait(vtimeout_ms) ⇒ Object
- #wakeup ⇒ Object
Constructor Details
#initialize ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'ext/winloop/winloop.c', line 205
static VALUE backend_initialize(VALUE self) {
winloop_backend *b;
TypedData_Get_Struct(self, winloop_backend, &backend_type, b);
if (!resolve_ntdll()) rb_raise(eError, "winloop: could not resolve ntdll entry points");
b->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!b->iocp) rb_raise(eError, "winloop: CreateIoCompletionPort failed (%lu)", GetLastError());
b->afd = afd_open();
if (b->afd == INVALID_HANDLE_VALUE) {
CloseHandle(b->iocp); b->iocp = NULL;
rb_raise(eError, "winloop: could not open \\Device\\Afd (unsupported platform?)");
}
if (!CreateIoCompletionPort(b->afd, b->iocp, AFD_KEY, 0)) {
CloseHandle(b->afd); CloseHandle(b->iocp); b->afd = INVALID_HANDLE_VALUE; b->iocp = NULL;
rb_raise(eError, "winloop: could not associate AFD handle (%lu)", GetLastError());
}
b->next_id = 0;
b->reqs = st_init_numtable();
b->closed = 0;
return self;
}
|
Instance Method Details
#cancel(vid) ⇒ Object
Cancel a pending poll. The cancelled op still posts a (STATUS_CANCELLED)
completion, which #wait reaps and frees — so we do NOT free here.
260 261 262 263 264 265 266 267 268 269 |
# File 'ext/winloop/winloop.c', line 260
static VALUE backend_cancel(VALUE self, VALUE vid) {
winloop_backend *b = get_backend(self);
uint64_t id = NUM2ULL(vid);
st_data_t val;
if (st_lookup(b->reqs, (st_data_t)id, &val)) {
winloop_req *req = (winloop_req *)val;
CancelIoEx(b->afd, &req->ov);
}
return Qnil;
}
|
#poll(io, vevents) ⇒ Object
Arm a one-shot readiness poll on ‘io` for `events`; returns the request id.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'ext/winloop/winloop.c', line 227
static VALUE backend_poll(VALUE self, VALUE io, VALUE vevents) {
winloop_backend *b = get_backend(self);
int events = NUM2INT(vevents);
int fd = rb_io_descriptor(io);
SOCKET sock = rb_w32_get_osfhandle(fd);
if (sock == INVALID_SOCKET || !rb_w32_is_socket(fd))
rb_raise(eError, "winloop: fd %d is not a socket (winloop polls sockets)", fd);
SOCKET base = base_socket(sock);
winloop_req *req = ALLOC(winloop_req);
memset(req, 0, sizeof(*req));
req->id = ++b->next_id;
req->in.Timeout.QuadPart = INT64_MAX; /* stays pending until an event fires */
req->in.NumberOfHandles = 1;
req->in.Exclusive = FALSE;
req->in.Handles[0].Handle = (HANDLE)base;
req->in.Handles[0].Events = ruby_to_afd(events);
NTSTATUS st = pNtDeviceIoControlFile(b->afd, NULL, NULL, &req->ov,
(PIO_STATUS_BLOCK)&req->ov.Internal, IOCTL_AFD_POLL,
&req->in, sizeof(req->in), &req->out, sizeof(req->out));
if (st != STATUS_PENDING && st != 0 /*STATUS_SUCCESS*/) {
xfree(req);
rb_raise(eError, "winloop: AFD poll failed (status 0x%08lX)", (unsigned long)st);
}
/* STATUS_SUCCESS == satisfied synchronously, but a completion packet is still
queued (we never set FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) — handle uniformly. */
st_insert(b->reqs, (st_data_t)req->id, (st_data_t)req);
return ULL2NUM(req->id);
}
|
#shutdown ⇒ Object
331 332 333 334 335 336 |
# File 'ext/winloop/winloop.c', line 331
static VALUE backend_shutdown(VALUE self) {
winloop_backend *b;
TypedData_Get_Struct(self, winloop_backend, &backend_type, b);
if (b) backend_drain_and_close(b);
return Qnil;
}
|
#wait(vtimeout_ms) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'ext/winloop/winloop.c', line 296
static VALUE backend_wait(VALUE self, VALUE vtimeout_ms) {
winloop_backend *b = get_backend(self);
DWORD ms = NIL_P(vtimeout_ms) ? INFINITE : (DWORD)NUM2ULONG(vtimeout_ms);
OVERLAPPED_ENTRY ents[REAP_CAP];
gqcs_args a = { b->iocp, ents, REAP_CAP, 0, ms, 0, FALSE };
rb_thread_call_without_gvl(gqcs_call, &a, gqcs_ubf, b->iocp);
BOOL ok = a.ok;
DWORD err = a.err;
ULONG n = a.n;
VALUE result = rb_ary_new();
if (!ok) {
if (err == WAIT_TIMEOUT) return result;
rb_raise(eError, "winloop: GetQueuedCompletionStatusEx failed (%lu)", err);
}
for (ULONG i = 0; i < n; i++) {
if (ents[i].lpCompletionKey == WAKE_KEY || ents[i].lpOverlapped == NULL) continue;
winloop_req *req = (winloop_req *)ents[i].lpOverlapped;
st_data_t key = (st_data_t)req->id;
st_delete(b->reqs, &key, NULL);
int events = afd_to_ruby(req->out.Handles[0].Events);
VALUE pair = rb_ary_new_from_args(2, ULL2NUM(req->id), INT2NUM(events));
rb_ary_push(result, pair);
xfree(req);
}
return result;
}
|
#wakeup ⇒ Object
325 326 327 328 329 |
# File 'ext/winloop/winloop.c', line 325
static VALUE backend_wakeup(VALUE self) {
winloop_backend *b = get_backend(self);
PostQueuedCompletionStatus(b->iocp, 0, WAKE_KEY, NULL);
return Qnil;
}
|