Class: Winipc::Pipe::Listener

Inherits:
Object
  • Object
show all
Defined in:
lib/winipc.rb,
ext/winipc/winipc.c

Instance Method Summary collapse

Instance Method Details

#_accept(vms) ⇒ Object

Listener#_accept(timeout_ms) -> Conn, or nil on timeout (-1 ms == INFINITE).



411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'ext/winipc/winipc.c', line 411

static VALUE
listener_accept(VALUE self, VALUE vms)
{
    listener_t *l = listener_get(self);
    long ms_in = NUM2LONG(vms);
    DWORD ms = (ms_in < 0) ? INFINITE : (DWORD)ms_in;
    OVERLAPPED ov;
    HANDLE cur;
    VALUE connobj;
    conn_t *c;
    DWORD gle, wgle = 0;
    int connected = 0;

    if (l->closed) rb_raise(eClosed, "winipc: listener is closed");
    if (l->pending == INVALID_HANDLE_VALUE)
        l->pending = listener_make_instance(l); /* recover if a prior accept failed mid-rotate */

    memset(&ov, 0, sizeof(ov));
    ov.hEvent = l->event;
    ResetEvent(l->event);

    if (ConnectNamedPipe(l->pending, &ov)) {
        connected = 1; /* completed synchronously (rare) */
    } else {
        gle = GetLastError();
        if (gle == ERROR_PIPE_CONNECTED) {
            connected = 1; /* client connected in the create/connect gap — success */
        } else if (gle == ERROR_IO_PENDING) {
            DWORD w = ov_wait(l->pending, &ov, ms, &wgle);
            DWORD n;
            if (w == WAIT_TIMEOUT) {
                CancelIoEx(l->pending, &ov);
                /* If a client connected during the cancel race, keep it rather
                 * than orphaning it; otherwise report the timeout. */
                if (GetOverlappedResult(l->pending, &ov, &n, TRUE))
                    connected = 1;
                else
                    return Qnil;
            } else if (w != WAIT_OBJECT_0) {
                ov_drain(l->pending, &ov);
                raise_gle("WaitForSingleObject", wgle);
            } else if (GetOverlappedResult(l->pending, &ov, &n, FALSE)) {
                connected = 1;
            } else {
                gle = GetLastError();
                if (gle == ERROR_OPERATION_ABORTED) return Qnil; /* interrupted */
                raise_gle("ConnectNamedPipe", gle);
            }
        } else {
            raise_gle("ConnectNamedPipe", gle);
        }
    }

    if (!connected) return Qnil;

    /* Move the connected instance into a Conn (so its free hook owns it), then
     * pre-create the next listening instance so the name stays live for the
     * next client (no ERROR_FILE_NOT_FOUND gap between accepts). */
    cur = l->pending;
    connobj = conn_alloc(cConn);
    c = conn_get(connobj);
    c->h = cur;
    c->is_server = 1;
    c->message_read = (l->pipe_mode & PIPE_READMODE_MESSAGE) ? 1 : 0;
    l->pending = INVALID_HANDLE_VALUE; /* ownership moved to the Conn */
    c->read_event = CreateEventW(NULL, TRUE, FALSE, NULL);
    c->write_event = CreateEventW(NULL, TRUE, FALSE, NULL);
    if (!c->read_event || !c->write_event) raise_gle("CreateEvent", GetLastError());

    l->pending = listener_make_instance(l);
    return connobj;
}

#accept(timeout: nil) ⇒ Object

Accept the next client. Blocks (cooperatively under a scheduler). With a timeout, returns nil if none connects in time.



177
178
179
# File 'lib/winipc.rb', line 177

def accept(timeout: nil)
  Winipc.run_blocking { _accept(Winipc.ms_for(timeout)) }
end

#closeObject



484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'ext/winipc/winipc.c', line 484

static VALUE
listener_close(VALUE self)
{
    listener_t *l = listener_get(self);
    if (l->pending && l->pending != INVALID_HANDLE_VALUE) {
        DisconnectNamedPipe(l->pending);
        CloseHandle(l->pending);
        l->pending = INVALID_HANDLE_VALUE;
    }
    if (l->event) { CloseHandle(l->event); l->event = NULL; }
    l->closed = 1;
    return Qnil;
}

#closed?Boolean

Returns:

  • (Boolean)


498
499
500
501
502
# File 'ext/winipc/winipc.c', line 498

static VALUE
listener_closed_p(VALUE self)
{
    return listener_get(self)->closed ? Qtrue : Qfalse;
}

#serveObject

Accept clients in a loop, yielding (and closing) each connection.



182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/winipc.rb', line 182

def serve
  loop do
    conn = accept
    break if conn.nil?

    begin
      yield conn
    ensure
      conn.close
    end
  end
end