summaryrefslogtreecommitdiff
path: root/ext/-test-/thread/instrumentation/instrumentation.c
blob: 517af7e33944d45a7126fea65b2a129008bee26e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "ruby/ruby.h"
#include "ruby/atomic.h"
#include "ruby/thread.h"

static rb_atomic_t started_count = 0;
static rb_atomic_t ready_count = 0;
static rb_atomic_t resumed_count = 0;
static rb_atomic_t suspended_count = 0;
static rb_atomic_t exited_count = 0;

void
ex_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data)
{
    switch (event) {
      case RUBY_INTERNAL_THREAD_EVENT_STARTED:
        RUBY_ATOMIC_INC(started_count);
        break;
      case RUBY_INTERNAL_THREAD_EVENT_READY:
        RUBY_ATOMIC_INC(ready_count);
        break;
      case RUBY_INTERNAL_THREAD_EVENT_RESUMED:
        RUBY_ATOMIC_INC(resumed_count);
        break;
      case RUBY_INTERNAL_THREAD_EVENT_SUSPENDED:
        RUBY_ATOMIC_INC(suspended_count);
        break;
      case RUBY_INTERNAL_THREAD_EVENT_EXITED:
        RUBY_ATOMIC_INC(exited_count);
        break;
    }
}

static rb_internal_thread_event_hook_t * single_hook = NULL;

static VALUE
thread_counters(VALUE thread)
{
    VALUE array = rb_ary_new2(5);
    rb_ary_push(array, UINT2NUM(started_count));
    rb_ary_push(array, UINT2NUM(ready_count));
    rb_ary_push(array, UINT2NUM(resumed_count));
    rb_ary_push(array, UINT2NUM(suspended_count));
    rb_ary_push(array, UINT2NUM(exited_count));
    return array;
}

static VALUE
thread_reset_counters(VALUE thread)
{
    RUBY_ATOMIC_SET(started_count, 0);
    RUBY_ATOMIC_SET(ready_count, 0);
    RUBY_ATOMIC_SET(resumed_count, 0);
    RUBY_ATOMIC_SET(suspended_count, 0);
    RUBY_ATOMIC_SET(exited_count, 0);
    return Qtrue;
}

static VALUE
thread_register_callback(VALUE thread)
{
    single_hook = rb_internal_thread_add_event_hook(
        ex_callback,
        RUBY_INTERNAL_THREAD_EVENT_STARTED |
        RUBY_INTERNAL_THREAD_EVENT_READY |
        RUBY_INTERNAL_THREAD_EVENT_RESUMED |
        RUBY_INTERNAL_THREAD_EVENT_SUSPENDED |
        RUBY_INTERNAL_THREAD_EVENT_EXITED,
        NULL
    );

    return Qnil;
}

static VALUE
thread_unregister_callback(VALUE thread)
{
    if (single_hook) {
        rb_internal_thread_remove_event_hook(single_hook);
        single_hook = NULL;
    }

    return Qnil;
}

static VALUE
thread_register_and_unregister_callback(VALUE thread)
{
    rb_internal_thread_event_hook_t * hooks[5];
    for (int i = 0; i < 5; i++) {
        hooks[i] = rb_internal_thread_add_event_hook(ex_callback, RUBY_INTERNAL_THREAD_EVENT_READY, NULL);
    }

    if (!rb_internal_thread_remove_event_hook(hooks[4])) return Qfalse;
    if (!rb_internal_thread_remove_event_hook(hooks[0])) return Qfalse;
    if (!rb_internal_thread_remove_event_hook(hooks[3])) return Qfalse;
    if (!rb_internal_thread_remove_event_hook(hooks[2])) return Qfalse;
    if (!rb_internal_thread_remove_event_hook(hooks[1])) return Qfalse;
    return Qtrue;
}

void
Init_instrumentation(void)
{
    VALUE mBug = rb_define_module("Bug");
    VALUE klass = rb_define_module_under(mBug, "ThreadInstrumentation");
    rb_define_singleton_method(klass, "counters", thread_counters, 0);
    rb_define_singleton_method(klass, "reset_counters", thread_reset_counters, 0);
    rb_define_singleton_method(klass, "register_callback", thread_register_callback, 0);
    rb_define_singleton_method(klass, "unregister_callback", thread_unregister_callback, 0);
    rb_define_singleton_method(klass, "register_and_unregister_callbacks", thread_register_and_unregister_callback, 0);
}