Class: Winproc::Process

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

Overview

---------------------------------------------------------------- Process ---

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Object

user-facing Process.new is forbidden; alloc exists only for safe GC.



242
243
244
245
246
247
248
249
# File 'ext/winproc/winproc.c', line 242

static VALUE
process_initialize(int argc, VALUE *argv, VALUE self)
{
    (void)argc; (void)argv; (void)self;
    rb_raise(eError, "winproc: processes are created by Winproc.spawn / "
                     "Winproc.pty / Winproc.runas");
    return self;
}

Instance Method Details

#_kill(*args) ⇒ Object

unreached



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
# File 'ext/winproc/winproc.c', line 427

static VALUE
process_kill(int argc, VALUE *argv, VALUE self)
{
    process_t *pr = process_live(self);
    VALUE vcode;
    UINT code = 1;
    DWORD r;
    rb_scan_args(argc, argv, "01", &vcode);
    if (!NIL_P(vcode)) code = (UINT)NUM2UINT(vcode);

    if (pr->exited) return self; /* no-op if already exited */
    r = WaitForSingleObject(pr->h, 0);
    if (r == WAIT_OBJECT_0) {
        DWORD c = 0;
        if (GetExitCodeProcess(pr->h, &c)) { pr->exit_code = c; pr->exited = 1; }
        return self; /* already gone */
    }
    if (!TerminateProcess(pr->h, code)) {
        DWORD gle = GetLastError();
        /* lost the race with natural exit: treat "already gone" as success */
        if (gle == ERROR_ACCESS_DENIED) {
            if (WaitForSingleObject(pr->h, 0) == WAIT_OBJECT_0) return self;
        }
        raise_gle("TerminateProcess", gle);
    }
    return self;
}

#_wait(vms) ⇒ Object

Process#_wait(ms) -> exit code Integer, or nil on timeout (-1 ms = INFINITE). Memoizes the exit code. Raises Closed if #close is in progress.



365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'ext/winproc/winproc.c', line 365

static VALUE
process_do_wait(VALUE self, VALUE vms)
{
    process_t *pr = process_get(self);
    pwait_args a;

    if (pr->exited) return ULONG2NUM(pr->exit_code); /* memoized; works after close */
    if (pr->closed) rb_raise(eClosed, "winproc: process is closed");

    a.pr = pr;
    a.ms_in = NUM2LL(vms); /* 64-bit: ms can exceed LONG_MAX on LLP64 (E-Slice) */
    InterlockedIncrement(&pr->waiters); /* under the GVL, before the region */
    return rb_ensure(process_wait_body, (VALUE)&a, process_wait_ensure, (VALUE)pr);
}

#alive?Boolean

Returns:

  • (Boolean)


387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'ext/winproc/winproc.c', line 387

static VALUE
process_alive_p(VALUE self)
{
    process_t *pr = process_live(self);
    DWORD r;
    if (pr->exited) return Qfalse;
    r = WaitForSingleObject(pr->h, 0);
    if (r == WAIT_OBJECT_0) {
        DWORD code = 0;
        if (GetExitCodeProcess(pr->h, &code)) { pr->exit_code = code; pr->exited = 1; }
        return Qfalse;
    }
    if (r == WAIT_TIMEOUT) return Qtrue;
    raise_gle("WaitForSingleObject", GetLastError());
    return Qfalse; /* unreached */
}

#closeObject



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'ext/winproc/winproc.c', line 482

static VALUE
process_close(VALUE self)
{
    process_t *pr = process_get(self);
    if (pr->closed) return Qnil;
    pr->closing = 1;
    if (pr->cancel_event) {
        pclose_t c; c.pr = pr;
        SetEvent(pr->cancel_event);
        rb_thread_call_without_gvl(pclose_spin_fn, &c, NULL, NULL);
    }
    process_memoize_exit(pr);
    if (pr->h != INVALID_HANDLE_VALUE) { CloseHandle(pr->h); pr->h = INVALID_HANDLE_VALUE; }
    if (pr->cancel_event) { CloseHandle(pr->cancel_event); pr->cancel_event = NULL; }
    pr->closed = 1;
    return Qnil;
}

#closed?Boolean

Returns:

  • (Boolean)


500
# File 'ext/winproc/winproc.c', line 500

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

#exitstatusObject

unreached



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'ext/winproc/winproc.c', line 404

static VALUE
process_exitstatus(VALUE self)
{
    process_t *pr = process_get(self);
    DWORD r;
    if (pr->exited) return ULONG2NUM(pr->exit_code);
    if (pr->closed) return Qnil; /* memoized value or nil after close */
    /* Only read GetExitCodeProcess after WaitForSingleObject reports signaled,
     * so 259 STILL_ACTIVE is never exposed as a real status (E-17). */
    r = WaitForSingleObject(pr->h, 0);
    if (r == WAIT_OBJECT_0) {
        DWORD code = 0;
        if (GetExitCodeProcess(pr->h, &code)) {
            pr->exit_code = code; pr->exited = 1;
            return ULONG2NUM(code);
        }
        raise_gle("GetExitCodeProcess", GetLastError());
    }
    if (r == WAIT_TIMEOUT) return Qnil; /* still running */
    raise_gle("WaitForSingleObject", GetLastError());
    return Qnil; /* unreached */
}

#kill(exit_code = 1) ⇒ Object

Hard kill (TerminateProcess); does NOT touch children (use a Job for trees).



299
300
301
# File 'lib/winproc.rb', line 299

def kill(exit_code = 1)
  _kill(exit_code)
end

#pidObject



380
381
382
383
384
385
# File 'ext/winproc/winproc.c', line 380

static VALUE
process_pid(VALUE self)
{
    /* never raises; works after close */
    return ULONG2NUM(process_get(self)->pid);
}

#stderrObject



306
# File 'lib/winproc.rb', line 306

def stderr = @stderr

#stdinObject

The writable/readable pipe ends, present only for :pipe stdio slots.



304
# File 'lib/winproc.rb', line 304

def stdin  = @stdin

#stdoutObject



305
# File 'lib/winproc.rb', line 305

def stdout = @stdout

#wait(timeout: nil) ⇒ Object

Block until the process exits; returns the exit code (Integer). timeout in seconds (nil = infinite); returns nil on timeout. Cooperative under a scheduler. Memoizes the exit code.



294
295
296
# File 'lib/winproc.rb', line 294

def wait(timeout: nil)
  Winproc.run_blocking { _wait(Winproc.ms_for(timeout)) }
end