Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
monitor.c
Go to the documentation of this file.
1#include "ruby/ruby.h"
2
3/* Thread::Monitor */
4
5struct rb_monitor {
6 long count;
7 const VALUE owner;
8 const VALUE mutex;
9};
10
11static void
12monitor_mark(void *ptr)
13{
14 struct rb_monitor *mc = ptr;
15 rb_gc_mark(mc->owner);
16 rb_gc_mark(mc->mutex);
17}
18
19static size_t
20monitor_memsize(const void *ptr)
21{
22 return sizeof(struct rb_monitor);
23}
24
25static const rb_data_type_t monitor_data_type = {
26 "monitor",
27 {monitor_mark, RUBY_TYPED_DEFAULT_FREE, monitor_memsize,},
29};
30
31static VALUE
32monitor_alloc(VALUE klass)
33{
34 struct rb_monitor *mc;
35 VALUE obj;
36
37 obj = TypedData_Make_Struct(klass, struct rb_monitor, &monitor_data_type, mc);
39 RB_OBJ_WRITE(obj, &mc->owner, Qnil);
40 mc->count = 0;
41
42 return obj;
43}
44
45static struct rb_monitor *
46monitor_ptr(VALUE monitor)
47{
48 struct rb_monitor *mc;
49 TypedData_Get_Struct(monitor, struct rb_monitor, &monitor_data_type, mc);
50 return mc;
51}
52
53static int
54mc_owner_p(struct rb_monitor *mc)
55{
56 return mc->owner == rb_thread_current();
57}
58
59static VALUE
60monitor_try_enter(VALUE monitor)
61{
62 struct rb_monitor *mc = monitor_ptr(monitor);
63
64 if (!mc_owner_p(mc)) {
65 if (!rb_mutex_trylock(mc->mutex)) {
66 return Qfalse;
67 }
68 RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current());
69 mc->count = 0;
70 }
71 mc->count += 1;
72 return Qtrue;
73}
74
75static VALUE
76monitor_enter(VALUE monitor)
77{
78 struct rb_monitor *mc = monitor_ptr(monitor);
79 if (!mc_owner_p(mc)) {
81 RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current());
82 mc->count = 0;
83 }
84 mc->count++;
85 return Qnil;
86}
87
88static VALUE
89monitor_check_owner(VALUE monitor)
90{
91 struct rb_monitor *mc = monitor_ptr(monitor);
92 if (!mc_owner_p(mc)) {
93 rb_raise(rb_eThreadError, "current thread not owner");
94 }
95 return Qnil;
96}
97
98static VALUE
99monitor_exit(VALUE monitor)
100{
101 monitor_check_owner(monitor);
102
103 struct rb_monitor *mc = monitor_ptr(monitor);
104
105 if (mc->count <= 0) rb_bug("monitor_exit: count:%d\n", (int)mc->count);
106 mc->count--;
107
108 if (mc->count == 0) {
109 RB_OBJ_WRITE(monitor, &mc->owner, Qnil);
111 }
112 return Qnil;
113}
114
115static VALUE
116monitor_locked_p(VALUE monitor)
117{
118 struct rb_monitor *mc = monitor_ptr(monitor);
119 return rb_mutex_locked_p(mc->mutex);
120}
121
122static VALUE
123monitor_owned_p(VALUE monitor)
124{
125 struct rb_monitor *mc = monitor_ptr(monitor);
126 return (rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc)) ? Qtrue : Qfalse;
127}
128
129static VALUE
130monitor_exit_for_cond(VALUE monitor)
131{
132 struct rb_monitor *mc = monitor_ptr(monitor);
133 long cnt = mc->count;
134 RB_OBJ_WRITE(monitor, &mc->owner, Qnil);
135 mc->count = 0;
136 return LONG2NUM(cnt);
137}
138
144};
145
146static VALUE
147monitor_wait_for_cond_body(VALUE v)
148{
149 struct wait_for_cond_data *data = (struct wait_for_cond_data *)v;
150 struct rb_monitor *mc = monitor_ptr(data->monitor);
151 // cond.wait(monitor.mutex, timeout)
152 rb_funcall(data->cond, rb_intern("wait"), 2, mc->mutex, data->timeout);
153 return Qtrue;
154}
155
156static VALUE
157monitor_enter_for_cond(VALUE v)
158{
159 // assert(rb_mutex_owned_p(mc->mutex) == Qtrue)
160 // but rb_mutex_owned_p is not exported...
161
162 struct wait_for_cond_data *data = (struct wait_for_cond_data *)v;
163 struct rb_monitor *mc = monitor_ptr(data->monitor);
165 mc->count = NUM2LONG(data->count);
166 return Qnil;
167}
168
169static VALUE
170monitor_wait_for_cond(VALUE monitor, VALUE cond, VALUE timeout)
171{
172 VALUE count = monitor_exit_for_cond(monitor);
173 struct wait_for_cond_data data = {
174 monitor,
175 cond,
176 timeout,
177 count,
178 };
179
180 return rb_ensure(monitor_wait_for_cond_body, (VALUE)&data,
181 monitor_enter_for_cond, (VALUE)&data);
182}
183
184static VALUE
185monitor_sync_body(VALUE monitor)
186{
187 return rb_yield_values(0);
188}
189
190static VALUE
191monitor_sync_ensure(VALUE monitor)
192{
193 return monitor_exit(monitor);
194}
195
196static VALUE
197monitor_synchronize(VALUE monitor)
198{
199 monitor_enter(monitor);
200 return rb_ensure(monitor_sync_body, monitor, monitor_sync_ensure, monitor);
201}
202
203void
205{
206 VALUE rb_cMonitor = rb_define_class("Monitor", rb_cObject);
207 rb_define_alloc_func(rb_cMonitor, monitor_alloc);
208
209 rb_define_method(rb_cMonitor, "try_enter", monitor_try_enter, 0);
210 rb_define_method(rb_cMonitor, "enter", monitor_enter, 0);
211 rb_define_method(rb_cMonitor, "exit", monitor_exit, 0);
212 rb_define_method(rb_cMonitor, "synchronize", monitor_synchronize, 0);
213
214 /* internal methods for MonitorMixin */
215 rb_define_method(rb_cMonitor, "mon_locked?", monitor_locked_p, 0);
216 rb_define_method(rb_cMonitor, "mon_check_owner", monitor_check_owner, 0);
217 rb_define_method(rb_cMonitor, "mon_owned?", monitor_owned_p, 0);
218
219 /* internal methods for MonitorMixin::ConditionalVariable */
220 rb_define_method(rb_cMonitor, "wait_for_cond", monitor_wait_for_cond, 2);
221}
struct RIMemo * ptr
Definition: debug.c:65
int count
Definition: encoding.c:57
VALUE rb_define_class(const char *, VALUE)
Defines a top-level class.
Definition: class.c:662
VALUE rb_cObject
Object class.
Definition: ruby.h:2012
VALUE rb_eThreadError
Definition: eval.c:924
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
void rb_bug(const char *fmt,...)
Definition: error.c:636
VALUE rb_ensure(VALUE(*)(VALUE), VALUE, VALUE(*)(VALUE), VALUE)
An equivalent to ensure clause.
Definition: eval.c:1115
void Init_monitor(void)
Definition: monitor.c:204
VALUE rb_mutex_new(void)
Definition: thread_sync.c:165
#define rb_yield_values(argc,...)
#define RUBY_TYPED_WB_PROTECTED
const VALUE VALUE obj
#define RUBY_TYPED_DEFAULT_FREE
unsigned long VALUE
VALUE rb_mutex_trylock(VALUE mutex)
Definition: thread_sync.c:203
VALUE rb_mutex_locked_p(VALUE mutex)
Definition: thread_sync.c:177
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
#define RB_OBJ_WRITE(a, slot, b)
#define LONG2NUM(x)
#define RUBY_TYPED_FREE_IMMEDIATELY
#define TypedData_Get_Struct(obj, type, data_type, sval)
#define rb_funcall(recv, mid, argc,...)
int VALUE v
void rb_gc_mark(VALUE)
Definition: gc.c:5228
#define rb_intern(str)
VALUE rb_mutex_unlock(VALUE mutex)
Definition: thread_sync.c:403
#define Qtrue
#define Qnil
#define Qfalse
VALUE rb_thread_current(void)
Definition: thread.c:2675
#define TypedData_Make_Struct(klass, type, data_type, sval)
#define NUM2LONG(x)
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
VALUE rb_mutex_lock(VALUE mutex)
Definition: thread_sync.c:333
rb_atomic_t cnt[RUBY_NSIG]
Definition: signal.c:503
long count
Definition: monitor.c:6
const VALUE mutex
Definition: monitor.c:8
const VALUE owner
Definition: monitor.c:7