mruby/c VM Source Code master (2026/08/06)
Loading...
Searching...
No Matches
c_task_queue.c
Go to the documentation of this file.
1
12
13/***** Feature test switches ************************************************/
14/***** System headers *******************************************************/
15//@cond
16#include "vm_config.h"
17#include <stdlib.h>
18#include <limits.h>
19#include <assert.h>
20//@endcond
21
22/***** Local headers ********************************************************/
23#include "mrubyc.h"
24
25/***** Constat values *******************************************************/
26/***** Macros ***************************************************************/
27/***** Typedefs *************************************************************/
28/***** Function prototypes **************************************************/
29/***** Local variables ******************************************************/
30/*
31 Unique sentinel returned by __pop_try when the current task is parked to
32 WAITING. It is never exposed to Ruby; identity is checked in C by __retry?.
33*/
35
36/*
37 Unique sentinel returned by __pop_try when a timeout elapsed before an item
38 became available. Never exposed to Ruby; identity is checked by __timeout?.
39*/
41
42/* Task::Error class, raised on illegal queue operations. */
44
45/* Cached instance variable symbols. */
48
49/***** Global variables *****************************************************/
50/***** Signal catching functions ********************************************/
51/***** Local functions ******************************************************/
52
53//================================================================
59static int queue_wake_one_waiter(void *q)
60{
61 int woke = 0;
62
63 mrbc_hal_disable_irq();
64 for( mrbc_tcb *tcb = mrbc_task_q_waiting_head(); tcb != NULL; tcb = tcb->next ) {
65 if( tcb->reason == TASKREASON_QUEUE && tcb->queue.target == q ) {
67 tcb->state = TASKSTATE_READY;
68 tcb->reason = 0;
69 tcb->queue.target = NULL;
70 tcb->queue.wakeup_tick = MRBC_WAIT_FOREVER;
72 woke = 1;
73 break;
74 }
75 }
76 mrbc_hal_enable_irq();
77
78 return woke;
79}
80
81
82//================================================================
88static int queue_wake_all_waiters(void *q)
89{
90 int woke = 0;
91
92 mrbc_hal_disable_irq();
94 while( tcb != NULL ) {
95 mrbc_tcb *next = tcb->next; // capture before the list is modified.
96 if( tcb->reason == TASKREASON_QUEUE && tcb->queue.target == q ) {
99 tcb->reason = 0;
100 tcb->queue.target = NULL;
103 woke = 1;
104 }
105 tcb = next;
106 }
107 mrbc_hal_enable_irq();
108
109 return woke;
110}
111
112
113//================================================================
123static int queue_is_closed(mrbc_value *queue)
124{
126 int is_closed = (mrbc_type(closed) == MRBC_TT_TRUE);
127 mrbc_decref(&closed);
128
129 return is_closed;
130}
131
132
133//================================================================
136static void c_task_queue_initialize(mrbc_vm *vm, mrbc_value v[], int argc)
137{
138 mrbc_value items = mrbc_array_new(vm, 0);
139 mrbc_instance_setiv(&v[0], sym_items_, &items);
140 mrbc_decref(&items);
141
142 mrbc_value closed = mrbc_false_value();
143 mrbc_instance_setiv(&v[0], sym_closed_, &closed);
144}
145
146
147//================================================================
150static void c_task_queue_push(mrbc_vm *vm, mrbc_value v[], int argc)
151{
152 switch( mrbc_task_queue_push(&v[0], &v[1]) ) {
154 break;
155
157 // hand control back so the woken task is selected by the scheduler.
159 break;
160
162 mrbc_raise(vm, task_error_class_, "queue closed");
163 break;
164
166 mrbc_raise(vm, MRBC_CLASS(ArgumentError), "invalid queue");
167 break;
168 }
169}
170
171
172//================================================================
186static void c_task_queue_pop_try(mrbc_vm *vm, mrbc_value v[], int argc)
187{
188 int non_block = (argc >= 1 && mrbc_type(v[1]) == MRBC_TT_TRUE);
189 int has_timeout = (argc >= 2 && mrbc_type(v[2]) == MRBC_TT_INTEGER);
190 uint32_t deadline = has_timeout ? (uint32_t)mrbc_integer(v[2]) : MRBC_WAIT_FOREVER;
191
193
194 // item available - return it.
195 if( mrbc_array_size(&items) > 0 ) {
196 mrbc_value ret = mrbc_array_shift(&items);
197 mrbc_decref(&items);
198 SET_RETURN(ret);
199 return;
200 }
201 mrbc_decref(&items);
202
203 // closed and empty.
204 if( queue_is_closed(&v[0]) ) {
206 return;
207 }
208
209 // non-blocking and empty.
210 if( non_block ) {
211 mrbc_raise(vm, task_error_class_, "queue empty");
212 return;
213 }
214
215 // timeout already elapsed (covers timeout_ms: 0) - give up without parking.
216 if( has_timeout && mrbc_deadline_reached(deadline) ) {
219 return;
220 }
221
222 // blocking: move the current task to WAITING and hand control back.
223 mrbc_tcb *tcb = mrbc_get_tcb(vm);
224 mrbc_hal_disable_irq();
228 tcb->queue.target = v[0].instance;
229 tcb->queue.wakeup_tick = deadline; // MRBC_WAIT_FOREVER when no timeout.
230 if( has_timeout ) mrbc_register_wakeup(deadline);
232 mrbc_hal_enable_irq();
233 tcb->vm.flag_preemption = 1;
234
235 // Return the hidden sentinel; the Ruby pop loop retries after wakeup.
238}
239
240
241//================================================================
247static void c_task_queue_is_wait_retry(mrbc_vm *vm, mrbc_value v[], int argc)
248{
249 int r = (mrbc_type(v[1]) == MRBC_TT_OBJECT &&
250 v[1].instance == wait_retry_.instance);
252}
253
254
255//================================================================
261static void c_task_queue_is_wait_timeout(mrbc_vm *vm, mrbc_value v[], int argc)
262{
263 int r = (mrbc_type(v[1]) == MRBC_TT_OBJECT &&
264 v[1].instance == wait_timeout_.instance);
266}
267
268
269//================================================================
279static void c_task_queue_deadline(mrbc_vm *vm, mrbc_value v[], int argc)
280{
281 int overflow = 0;
282 uint32_t deadline = mrbc_deadline_after_ms(mrbc_integer(v[1]), &overflow);
283 if( overflow ) {
284 mrbc_raise(vm, MRBC_CLASS(RangeError), "timeout_ms is too large");
285 return;
286 }
287 SET_INT_RETURN((mrbc_int_t)deadline);
288}
289
290
291//================================================================
294static void c_task_queue_size(mrbc_vm *vm, mrbc_value v[], int argc)
295{
297 int n = mrbc_array_size(&items);
298 mrbc_decref(&items);
300}
301
302
303//================================================================
306static void c_task_queue_empty_q(mrbc_vm *vm, mrbc_value v[], int argc)
307{
309 int empty = (mrbc_array_size(&items) == 0);
310 mrbc_decref(&items);
311 SET_BOOL_RETURN(empty);
312}
313
314
315//================================================================
318static void c_task_queue_clear(mrbc_vm *vm, mrbc_value v[], int argc)
319{
321 mrbc_array_clear(&items);
322 mrbc_decref(&items);
323 // returns self.
324}
325
326
327//================================================================
330static void c_task_queue_close(mrbc_vm *vm, mrbc_value v[], int argc)
331{
332 if( !queue_is_closed(&v[0]) ) {
335 if( queue_wake_all_waiters(v[0].instance) ) {
337 }
338 }
339 // returns self.
340}
341
342
343//================================================================
346static void c_task_queue_closed_q(mrbc_vm *vm, mrbc_value v[], int argc)
347{
349}
350
351
352//================================================================
355static void c_task_queue_num_waiting(mrbc_vm *vm, mrbc_value v[], int argc)
356{
357 int count = 0;
358
359 mrbc_hal_disable_irq();
360 for( mrbc_tcb *tcb = mrbc_task_q_waiting_head(); tcb != NULL; tcb = tcb->next ) {
361 if( tcb->reason == TASKREASON_QUEUE && tcb->queue.target == v[0].instance ) {
362 count++;
363 }
364 }
365 mrbc_hal_enable_irq();
366
367 SET_INT_RETURN(count);
368}
369
370
371/* MRBC_AUTOGEN_METHOD_TABLE
372
373 CLASS("Task::Queue")
374 FILE("_autogen_class_task_queue.h")
375
376 METHOD( "initialize", c_task_queue_initialize )
377 METHOD( "__push", c_task_queue_push )
378 METHOD( "__pop_try", c_task_queue_pop_try )
379 METHOD( "__retry?", c_task_queue_is_wait_retry )
380 METHOD( "__timeout?", c_task_queue_is_wait_timeout )
381 METHOD( "__deadline", c_task_queue_deadline )
382 METHOD( "size", c_task_queue_size )
383 METHOD( "length", c_task_queue_size )
384 METHOD( "empty?", c_task_queue_empty_q )
385 METHOD( "clear", c_task_queue_clear )
386 METHOD( "close", c_task_queue_close )
387 METHOD( "closed?", c_task_queue_closed_q )
388 METHOD( "num_waiting", c_task_queue_num_waiting )
389*/
390#include "_autogen_class_task_queue.h"
391
392
393/***** Global functions *****************************************************/
394
395//================================================================
399{
400 // Register Task::Queue (builtin class) as a constant under Task.
401 mrbc_value vcls = mrbc_immediate_value(MRBC_TT_CLASS, .cls = MRBC_CLASS(Task_Queue));
402 mrbc_set_class_const(MRBC_CLASS(Task), MRBC_SYM(Queue), &vcls);
403
404 // Define Task::Error < StandardError.
406 MRBC_CLASS(StandardError));
407
408 // Cache instance variable symbols.
409 sym_items_ = mrbc_str_to_symid("@items");
410 sym_closed_ = mrbc_str_to_symid("@closed");
411
412 // Create the unique, private sentinels (kept for the process life).
415}
416
417
418//================================================================
442{
443 if( mrbc_type(*queue) != MRBC_TT_OBJECT ||
444 !mrbc_obj_is_kind_of(queue, MRBC_CLASS(Task_Queue)) ) {
446 }
447
449
451 assert( mrbc_type(items) == MRBC_TT_ARRAY );
452 mrbc_incref(value);
453 mrbc_array_push(&items, value);
454 mrbc_decref(&items);
455
456 return queue_wake_one_waiter(queue->instance) ?
458}
#define mrbc_true_value()
Definition boxing_no.h:71
#define mrbc_immediate_value(...)
Definition boxing_no.h:75
#define mrbc_false_value()
Definition boxing_no.h:72
#define mrbc_type(o)
Definition boxing_no.h:57
struct RObject mrbc_value
Value object. Default version.
#define mrbc_integer(o)
Definition boxing_no.h:58
int mrbc_array_push(mrbc_value *ary, mrbc_value *set_val)
Definition c_array.c:243
mrbc_value mrbc_array_new(mrbc_vm *vm, int size)
Definition c_array.c:83
mrbc_value mrbc_array_shift(mrbc_value *ary)
Definition c_array.c:316
void mrbc_array_clear(mrbc_value *ary)
Definition c_array.c:409
static int mrbc_array_size(const mrbc_value *ary)
Definition c_array.h:84
static int queue_is_closed(mrbc_value *queue)
static mrbc_value wait_retry_
static int queue_wake_all_waiters(void *q)
static mrbc_sym sym_closed_
static mrbc_sym sym_items_
static int queue_wake_one_waiter(void *q)
static mrbc_value wait_timeout_
mrbc_task_queue_push_result mrbc_task_queue_push(mrbc_value *queue, mrbc_value *value)
void mrbc_init_task_queue(void)
static struct RClass * task_error_class_
mrbc_task_queue_push_result
Result of mrbc_task_queue_push().
@ MRBC_TASK_QUEUE_PUSH_OK
pushed. no task was waiting.
@ MRBC_TASK_QUEUE_PUSH_CLOSED
the queue is closed.
@ MRBC_TASK_QUEUE_PUSH_OK_WOKE
pushed and a waiting task was woken.
@ MRBC_TASK_QUEUE_PUSH_INVALID
not a Task::Queue instance.
mrbc_value mrbc_instance_new(struct VM *vm, mrbc_class *cls, int size)
Definition class.c:281
int mrbc_obj_is_kind_of(const mrbc_value *obj, const mrbc_class *tcls)
Definition class.c:393
mrbc_value mrbc_instance_getiv(mrbc_value *target, mrbc_sym sym_id)
Definition class.c:350
mrbc_class * mrbc_define_class_under(struct VM *vm, const mrbc_class *outer, const char *name, mrbc_class *super)
Definition class.c:207
int mrbc_instance_setiv(mrbc_value *target, mrbc_sym sym_id, mrbc_value *v)
Definition class.c:322
#define MRBC_CLASS(cls)
Definition class.h:55
void mrbc_raise(struct VM *vm, struct RClass *exc_cls, const char *msg)
Definition error.c:145
int mrbc_set_class_const(const mrbc_class *cls, mrbc_sym sym_id, mrbc_value *v)
Definition global.c:76
Include at once the necessary header files.
void mrbc_task_q_insert(mrbc_tcb *p_tcb)
Definition rrt0.c:94
mrbc_tcb * mrbc_task_q_waiting_head(void)
Definition rrt0.c:163
uint32_t mrbc_deadline_after_ms(mrbc_int_t ms, int *p_overflow)
Definition rrt0.c:603
int mrbc_deadline_reached(uint32_t deadline)
Definition rrt0.c:625
void mrbc_register_wakeup(uint32_t wakeup_tick)
Definition rrt0.c:639
void mrbc_task_q_delete(mrbc_tcb *p_tcb)
Definition rrt0.c:128
struct RTcb mrbc_tcb
Task control block.
@ TASKSTATE_READY
Ready.
Definition rrt0.h:47
@ TASKSTATE_WAITING
Waiting.
Definition rrt0.h:49
static mrbc_tcb * mrbc_get_tcb(const mrbc_vm *vm)
Definition rrt0.h:179
@ TASKREASON_QUEUE
Definition rrt0.h:57
#define MRBC_WAIT_FOREVER
Definition rrt0.h:66
Class object.
Definition class.h:87
struct RInstance * instance
Definition boxing_no.h:29
void * target
Task::Queue instance waited on (TASKREASON_QUEUE).
Definition rrt0.h:98
struct VM vm
Definition rrt0.h:105
uint8_t state
task state. defined in MrbcTaskState.
Definition rrt0.h:90
uint8_t reason
sub state. defined in MrbcTaskReason.
Definition rrt0.h:91
uint32_t wakeup_tick
wakeup time for sleep state.
Definition rrt0.h:95
struct RTcb::@363107132000246361337323266072372261072345062300::@123367244141214366021077021374060365005063270262 queue
queue wait state (TASKREASON_QUEUE).
struct RTcb * next
daisy chain in task queue.
Definition rrt0.h:86
volatile int8_t flag_preemption
Definition vm.h:155
mrbc_sym mrbc_str_to_symid(const char *str)
Definition symbol.c:218
#define MRBC_SYM(sym)
Definition symbol.h:34
int32_t mrbc_int_t
Definition value.h:47
static void mrbc_decref(mrbc_value *v)
Definition value.h:572
#define SET_BOOL_RETURN(n)
Definition value.h:238
#define SET_INT_RETURN(n)
Definition value.h:243
#define SET_NIL_RETURN()
Definition value.h:226
static void mrbc_incref(mrbc_value *v)
Definition value.h:557
int16_t mrbc_sym
mruby/c symbol ID
Definition value.h:62
@ MRBC_TT_INTEGER
Integer.
Definition value.h:86
@ MRBC_TT_OBJECT
General instance.
Definition value.h:95
@ MRBC_TT_ARRAY
Array.
Definition value.h:97
@ MRBC_TT_TRUE
TrueClass.
Definition value.h:85
@ MRBC_TT_CLASS
Class.
Definition value.h:90
#define SET_RETURN(n)
Definition value.h:221
struct VM mrbc_vm
Virtual Machine.
Global configuration of mruby/c VM's.