Class: Winloop::Backend

Inherits:
Object
  • Object
show all
Defined in:
ext/winloop/winloop.c

Instance Method Summary collapse

Constructor Details

#initializeObject



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);
}

#shutdownObject



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;
}

#wakeupObject



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;
}