Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
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);
38 RB_OBJ_WRITE(obj, &mc->mutex, rb_mutex_new());
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_fiber_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_fiber_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_fiber_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 fiber 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#ifdef HAVE_RB_EXT_RACTOR_SAFE
207 rb_ext_ractor_safe(true);
208#endif
209
210 VALUE rb_cMonitor = rb_define_class("Monitor", rb_cObject);
211 rb_define_alloc_func(rb_cMonitor, monitor_alloc);
212
213 rb_define_method(rb_cMonitor, "try_enter", monitor_try_enter, 0);
214 rb_define_method(rb_cMonitor, "enter", monitor_enter, 0);
215 rb_define_method(rb_cMonitor, "exit", monitor_exit, 0);
216 rb_define_method(rb_cMonitor, "synchronize", monitor_synchronize, 0);
217
218 /* internal methods for MonitorMixin */
219 rb_define_method(rb_cMonitor, "mon_locked?", monitor_locked_p, 0);
220 rb_define_method(rb_cMonitor, "mon_check_owner", monitor_check_owner, 0);
221 rb_define_method(rb_cMonitor, "mon_owned?", monitor_owned_p, 0);
222
223 /* internal methods for MonitorMixin::ConditionalVariable */
224 rb_define_method(rb_cMonitor, "wait_for_cond", monitor_wait_for_cond, 2);
225}
VALUE rb_fiber_current(void)
Definition: cont.c:2182
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
struct RIMemo * ptr
Definition: debug.c:88
VALUE rb_eThreadError
Definition: eval.c:953
void rb_gc_mark(VALUE ptr)
Definition: gc.c:6112
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:748
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
void rb_bug(const char *fmt,...)
Definition: error.c:768
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1148
VALUE rb_cObject
Object class.
Definition: object.c:49
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
void rb_ext_ractor_safe(bool flag)
Definition: load.c:1058
VALUE rb_mutex_new(void)
Definition: thread_sync.c:177
VALUE rb_mutex_trylock(VALUE mutex)
Definition: thread_sync.c:236
VALUE rb_mutex_locked_p(VALUE mutex)
Definition: thread_sync.c:189
VALUE rb_mutex_unlock(VALUE mutex)
Definition: thread_sync.c:474
VALUE rb_mutex_lock(VALUE mutex)
Definition: thread_sync.c:402
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
ID rb_intern(const char *)
Definition: symbol.c:785
VALUE rb_yield_values(int n,...)
Definition: vm_eval.c:1353
#define LONG2NUM
Definition: long.h:50
#define NUM2LONG
Definition: long.h:51
void Init_monitor(void)
Definition: monitor.c:204
int count
Definition: nkf.c:5055
#define RB_OBJ_WRITE(a, slot, b)
WB for new reference from ‘a’ to ‘b’.
Definition: rgengc.h:107
#define RUBY_TYPED_DEFAULT_FREE
Definition: rtypeddata.h:44
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
@ RUBY_TYPED_WB_PROTECTED
Definition: rtypeddata.h:64
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
rb_atomic_t cnt[RUBY_NSIG]
Definition: signal.c:508
#define Qtrue
#define Qnil
#define Qfalse
long count
Definition: monitor.c:6
const VALUE mutex
Definition: monitor.c:8
const VALUE owner
Definition: monitor.c:7
unsigned long VALUE
Definition: value.h:38