Class: Winreg::Watch

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

Overview

A change-notification registration (constructed only via Key#watch). Armed at construction and re-armed before every delivery, so no final state is ever missed; deliveries coalesce (:changed means ">= 1 matching change since the previous delivery") and carry no payload.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._new(vroot, vsub, vview, vsubtree, vfilter) ⇒ Object

Watch._new(root, subpath_or_nil, view_sam, subtree, filter) — private singleton. Opens its OWN handle (KEY_NOTIFY | view) by path, creates the two private events, and arms the first registration so changes between construction and the first #wait are caught. Any failure: capture code, close what was opened, raise.



620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'ext/winreg/winreg.c', line 620

static VALUE
watch_s_new(VALUE klass, VALUE vroot, VALUE vsub, VALUE vview, VALUE vsubtree, VALUE vfilter)
{
    VALUE obj = watch_alloc(cWatch);
    watch_t *w = watch_get(obj);
    HKEY root = root_hkey(vroot);
    DWORD view = (DWORD)NUM2ULONG(vview);
    DWORD filter = (DWORD)NUM2ULONG(vfilter) | REG_NOTIFY_THREAD_AGNOSTIC;
    BOOL subtree = RTEST(vsubtree) ? TRUE : FALSE;
    HKEY h = NULL;
    LONG rc;
    DWORD gle;

    if (NIL_P(vsub)) {
        /* bare root: the predefined pseudo-handle itself carries KEY_NOTIFY */
        h = root;
        w->predefined = 1;
    } else {
        WCHAR *ws = to_wide(vsub);
        rc = RegOpenKeyExW(root, ws, 0, KEY_NOTIFY | view, &h);
        xfree(ws);
        if (rc != ERROR_SUCCESS) raise_lstatus("RegOpenKeyExW", rc);
    }
    w->hkey = h; /* owned from here: free hook / teardown closes it */

    w->notify_event = CreateEventW(NULL, FALSE, FALSE, NULL); /* auto-reset */
    if (!w->notify_event) {
        gle = GetLastError();
        watch_teardown(w);
        raise_gle("CreateEvent", gle);
    }
    w->stop_event = CreateEventW(NULL, TRUE, FALSE, NULL); /* manual-reset */
    if (!w->stop_event) {
        gle = GetLastError();
        watch_teardown(w);
        raise_gle("CreateEvent", gle);
    }
    rc = RegNotifyChangeKeyValue(w->hkey, subtree, filter, w->notify_event, TRUE);
    if (rc != ERROR_SUCCESS) {
        watch_teardown(w);
        raise_lstatus("RegNotifyChangeKeyValue", rc);
    }
    w->filter = filter;
    w->subtree = subtree;
    w->closed = 0;
    return obj;
}

Instance Method Details

#closeObject

Watch#close — step 5. Idempotent; unblocks a concurrent #wait (which then raises Closed in the waiting thread). Handles MUST NOT be closed while another thread waits on them: if waiting, the teardown is deferred to the waiter's next wake (step 4a, which every wake path executes). waiting is only ever cleared with the GVL held, so waiting == 0 guarantees no thread is blocked on the handles.



805
806
807
808
809
810
811
812
813
814
815
816
817
# File 'ext/winreg/winreg.c', line 805

static VALUE
watch_close(VALUE self)
{
    watch_t *w = watch_get(self);
    if (w->closed) return Qnil;
    w->closed = 1;
    if (w->waiting) {
        SetEvent(w->stop_event);
        return Qnil;
    }
    watch_teardown(w);
    return Qnil;
}

#closed?Boolean

Returns:

  • (Boolean)


819
# File 'ext/winreg/winreg.c', line 819

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

#wait(timeout: nil) ⇒ Object

-> :changed (rearmed; ready to wait again) :deleted (watched key was deleted; the watch auto-closes) nil (timeout elapsed; registration still armed) timeout in seconds (nil = infinite). Cooperates with a fiber scheduler via Winreg.run_blocking; standalone it releases the GVL and is interruptible. Raises Closed if closed (incl. closed by another thread mid-wait); Winreg::Error if another wait is already in flight (single-waiter); OSError on a rearm failure other than ERROR_KEY_DELETED.



651
652
653
654
# File 'lib/winreg.rb', line 651

def wait(timeout: nil)
  ms = Winreg.ms_for(timeout)
  Winreg.run_blocking { _wait(ms) }
end