Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
fiber.c
Go to the documentation of this file.
1 #include "mruby.h"
2 #include "mruby/array.h"
3 #include "mruby/class.h"
4 #include "mruby/proc.h"
5 
6 #define FIBER_STACK_INIT_SIZE 64
7 #define FIBER_CI_INIT_SIZE 8
8 
9 /*
10  * call-seq:
11  * Fiber.new{...} -> obj
12  *
13  * Creates a fiber, whose execution is suspend until it is explicitly
14  * resumed using <code>Fiber#resume</code> method.
15  * The code running inside the fiber can give up control by calling
16  * <code>Fiber.yield</code> in which case it yields control back to caller
17  * (the caller of the <code>Fiber#resume</code>).
18  *
19  * Upon yielding or termination the Fiber returns the value of the last
20  * executed expression
21  *
22  * For instance:
23  *
24  * fiber = Fiber.new do
25  * Fiber.yield 1
26  * 2
27  * end
28  *
29  * puts fiber.resume
30  * puts fiber.resume
31  * puts fiber.resume
32  *
33  * <em>produces</em>
34  *
35  * 1
36  * 2
37  * resuming dead fiber (RuntimeError)
38  *
39  * The <code>Fiber#resume</code> method accepts an arbitrary number of
40  * parameters, if it is the first call to <code>resume</code> then they
41  * will be passed as block arguments. Otherwise they will be the return
42  * value of the call to <code>Fiber.yield</code>
43  *
44  * Example:
45  *
46  * fiber = Fiber.new do |first|
47  * second = Fiber.yield first + 2
48  * end
49  *
50  * puts fiber.resume 10
51  * puts fiber.resume 14
52  * puts fiber.resume 18
53  *
54  * <em>produces</em>
55  *
56  * 12
57  * 14
58  * resuming dead fiber (RuntimeError)
59  *
60  */
61 static mrb_value
62 fiber_init(mrb_state *mrb, mrb_value self)
63 {
64  static const struct mrb_context mrb_context_zero = { 0 };
65  struct RFiber *f = (struct RFiber*)mrb_ptr(self);
66  struct mrb_context *c;
67  struct RProc *p;
68  mrb_callinfo *ci;
69  mrb_value blk;
70 
71  mrb_get_args(mrb, "&", &blk);
72 
73  if (mrb_nil_p(blk)) {
74  mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block");
75  }
76  p = mrb_proc_ptr(blk);
77  if (MRB_PROC_CFUNC_P(p)) {
78  mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber from C defined method");
79  }
80 
81  f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
82  *f->cxt = mrb_context_zero;
83  c = f->cxt;
84 
85  /* initialize VM stack */
88  c->stack = c->stbase;
89 
90  /* copy receiver from a block */
91  c->stack[0] = mrb->c->stack[0];
92 
93  /* initialize callinfo stack */
96  c->ci = c->cibase;
97 
98  /* adjust return callinfo */
99  ci = c->ci;
100  ci->target_class = p->target_class;
101  ci->proc = p;
102  ci->pc = p->body.irep->iseq;
103  ci->nregs = p->body.irep->nregs;
104  ci[1] = ci[0];
105  c->ci++; /* push dummy callinfo */
106 
107  c->fib = f;
109 
110  return self;
111 }
112 
113 static struct mrb_context*
114 fiber_check(mrb_state *mrb, mrb_value fib)
115 {
116  struct RFiber *f = (struct RFiber*)mrb_ptr(fib);
117 
118  if (!f->cxt) {
119  mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized Fiber");
120  }
121  return f->cxt;
122 }
123 
124 static mrb_value
125 fiber_result(mrb_state *mrb, mrb_value *a, int len)
126 {
127  if (len == 0) return mrb_nil_value();
128  if (len == 1) return a[0];
129  return mrb_ary_new_from_values(mrb, len, a);
130 }
131 
132 /* mark return from context modifying method */
133 #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL
134 
135 /*
136  * call-seq:
137  * fiber.resume(args, ...) -> obj
138  *
139  * Resumes the fiber from the point at which the last <code>Fiber.yield</code>
140  * was called, or starts running it if it is the first call to
141  * <code>resume</code>. Arguments passed to resume will be the value of
142  * the <code>Fiber.yield</code> expression or will be passed as block
143  * parameters to the fiber's block if this is the first <code>resume</code>.
144  *
145  * Alternatively, when resume is called it evaluates to the arguments passed
146  * to the next <code>Fiber.yield</code> statement inside the fiber's block
147  * or to the block value if it runs to completion without any
148  * <code>Fiber.yield</code>
149  */
150 static mrb_value
151 fiber_resume(mrb_state *mrb, mrb_value self)
152 {
153  struct mrb_context *c = fiber_check(mrb, self);
154  mrb_value *a;
155  int len;
156 
157  if (c->status == MRB_FIBER_RESUMED) {
158  mrb_raise(mrb, E_RUNTIME_ERROR, "double resume");
159  }
160  if (c->status == MRB_FIBER_TERMINATED) {
161  mrb_raise(mrb, E_RUNTIME_ERROR, "resuming dead fiber");
162  }
163  mrb_get_args(mrb, "*", &a, &len);
164  mrb->c->status = MRB_FIBER_RESUMED;
165  if (c->status == MRB_FIBER_CREATED) {
166  mrb_value *b = c->stack+1;
167  mrb_value *e = b + len;
168 
169  while (b<e) {
170  *b++ = *a++;
171  }
172  c->cibase->argc = len;
173  c->prev = mrb->c;
174  if (c->prev->fib)
175  mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
176  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
178  mrb->c = c;
179 
181  return c->ci->proc->env->stack[0];
182  }
184  c->prev = mrb->c;
185  if (c->prev->fib)
186  mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
187  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
189  mrb->c = c;
190  return fiber_result(mrb, a, len);
191 }
192 
193 /*
194  * call-seq:
195  * fiber.alive? -> true or false
196  *
197  * Returns true if the fiber can still be resumed. After finishing
198  * execution of the fiber block this method will always return false.
199  */
200 static mrb_value
201 fiber_alive_p(mrb_state *mrb, mrb_value self)
202 {
203  struct mrb_context *c = fiber_check(mrb, self);
204  return mrb_bool_value(c->status != MRB_FIBER_TERMINATED);
205 }
206 
207 /*
208  * call-seq:
209  * Fiber.yield(args, ...) -> obj
210  *
211  * Yields control back to the context that resumed the fiber, passing
212  * along any arguments that were passed to it. The fiber will resume
213  * processing at this point when <code>resume</code> is called next.
214  * Any arguments passed to the next <code>resume</code> will be the
215  * value that this <code>Fiber.yield</code> expression evaluates to.
216  */
217 static mrb_value
218 fiber_yield(mrb_state *mrb, mrb_value self)
219 {
220  struct mrb_context *c = mrb->c;
221  mrb_value *a;
222  int len;
223 
224  if (!c->prev) {
225  mrb_raise(mrb, E_ARGUMENT_ERROR, "can't yield from root fiber");
226  }
227  mrb_get_args(mrb, "*", &a, &len);
229  mrb->c = c->prev;
230  c->prev = NULL;
231  MARK_CONTEXT_MODIFY(mrb->c);
232  return fiber_result(mrb, a, len);
233 }
234 
235 void
237 {
238  struct RClass *c;
239 
240  c = mrb_define_class(mrb, "Fiber", mrb->object_class);
242 
243  mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE());
244  mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY());
245  mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE());
246 
247  mrb_define_class_method(mrb, c, "yield", fiber_yield, MRB_ARGS_ANY());
248 }
249 
250 void
252 {
253 }