1 /*
2 Copyright (C) 2007-2015, Parrot Foundation.
3
4 =head1 NAME
5
6 src/scheduler.c - The core routines for the concurrency scheduler
7
8 =head1 DESCRIPTION
9
10 Each interpreter has a concurrency scheduler element in its core struct. The
11 scheduler is responsible for receiving, dispatching, and monitoring events,
12 exceptions, async I/O, and concurrent tasks (threads).
13
14 =cut
15
16 */
17
18 #include "parrot/parrot.h"
19 #include "parrot/extend.h"
20 #include "parrot/scheduler_private.h"
21 #include "parrot/runcore_api.h"
22 #include "parrot/alarm.h"
23 #include "parrot/scheduler.h"
24 #include "parrot/thread.h"
25
26 #include "pmc/pmc_scheduler.h"
27 #include "pmc/pmc_task.h"
28 #include "pmc/pmc_timer.h"
29 #include "pmc/pmc_alarm.h"
30 #include "pmc/pmc_pmclist.h"
31 #include "pmc/pmc_continuation.h"
32
33 #include "scheduler.str"
34
35 /* HEADERIZER HFILE: include/parrot/scheduler.h */
36
37 /* HEADERIZER BEGIN: static */
38 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
39
40 static int Parrot_cx_preemption_enabled(PARROT_INTERP)
41 __attribute__nonnull__(1);
42
43 #define ASSERT_ARGS_Parrot_cx_preemption_enabled __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
44 PARROT_ASSERT_ARG(interp))
45 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
46 /* HEADERIZER END: static */
47
48 /*
49
50 =head2 Scheduler Interface Functions
51
52 Functions to interface with the concurrency scheduler.
53
54 =over 4
55
56 =item C<void Parrot_cx_init_scheduler(PARROT_INTERP)>
57
58 Initialize the concurrency scheduler for the interpreter.
59
60 =cut
61
62 */
63
64 void
Parrot_cx_init_scheduler(PARROT_INTERP)65 Parrot_cx_init_scheduler(PARROT_INTERP)
66 {
67 ASSERT_ARGS(Parrot_cx_init_scheduler)
68 interp->quantum_done = Parrot_floatval_time() + PARROT_TASK_SWITCH_QUANTUM;
69
70 interp->scheduler = Parrot_pmc_new(interp, enum_class_Scheduler);
71
72 if (!interp->parent_interpreter) { /* only run once, for the master interp */
73 Parrot_alarm_init();
74
75 Parrot_thread_init_threads_array(interp);
76 Parrot_thread_insert_thread(interp, interp, 0);
77 }
78 }
79
80 /*
81
82 =item C<void Parrot_cx_begin_execution(PARROT_INTERP, PMC *main, PMC *argv)>
83
84 Construct the main task, add it to the task queue, and then execute tasks
85 until the task queue becomes empty.
86
87 =cut
88
89 */
90
91 PARROT_EXPORT
92 void
Parrot_cx_begin_execution(PARROT_INTERP,ARGIN (PMC * main),ARGIN (PMC * argv))93 Parrot_cx_begin_execution(PARROT_INTERP, ARGIN(PMC *main), ARGIN(PMC *argv))
94 {
95 ASSERT_ARGS(Parrot_cx_begin_execution)
96 PMC * const scheduler = interp->scheduler;
97 Parrot_Scheduler_attributes * const sched = PARROT_SCHEDULER(scheduler);
98 PMC * const main_task = Parrot_pmc_new(interp, enum_class_Task);
99 Parrot_Task_attributes * const tdata = PARROT_TASK(main_task);
100 INTVAL task_count;
101
102 tdata->code = main;
103 tdata->data = argv;
104 PARROT_GC_WRITE_BARRIER(interp, main_task);
105
106 SCHEDULER_enable_scheduler_SET(scheduler);
107
108 Parrot_cx_schedule_immediate(interp, main_task);
109 Parrot_cx_outer_runloop(interp);
110
111 task_count = VTABLE_get_integer(interp, sched->all_tasks);
112 if (task_count > 0)
113 Parrot_warn(interp, PARROT_WARNINGS_ALL_FLAG,
114 "Exiting with %d active tasks.\n", task_count);
115 }
116
117 /*
118
119 =item C<void Parrot_cx_outer_runloop(PARROT_INTERP)>
120
121 This is the core loop performed by each active OS thread. If it's the
122 thread that needs to be running, it invokes the Scheduler to pick a
123 task.
124
125 =cut
126
127 */
128
129 void
Parrot_cx_outer_runloop(PARROT_INTERP)130 Parrot_cx_outer_runloop(PARROT_INTERP)
131 {
132 ASSERT_ARGS(Parrot_cx_outer_runloop)
133 PMC * const scheduler = interp->scheduler;
134 Parrot_Scheduler_attributes * const sched = PARROT_SCHEDULER(scheduler);
135 INTVAL alarm_count, foreign_count, i;
136
137 /* Main loop. Continue to loop so long as we have any tasks, any alarms,
138 or any foreign tasks to execute. If we have none of these things, exit. */
139 do {
140 /* If we have tasks in the scheduler, run them in a loop until there
141 are no more. */
142 while (VTABLE_get_integer(interp, scheduler) > 0) {
143 /* there can be no active runloops at this point, so it should be save
144 * to start counting at 0 again. This way the continuation in the next
145 * task will find a runloop with id 1 when encountering an exception */
146 interp->current_runloop_level = 0;
147 reset_runloop_id_counter(interp);
148
149 Parrot_cx_next_task(interp, scheduler);
150
151 /* add expired alarms to the task queue */
152 Parrot_cx_check_alarms(interp, interp->scheduler);
153 }
154
155 /* Loop over all foreign tasks in the scheduler. If the foreign task
156 is killed, remove it from the scheduler. */
157 foreign_count = VTABLE_get_integer(interp, sched->foreign_tasks);
158 for (i = 0; i < foreign_count; i++) {
159 PMC * const task = VTABLE_get_pmc_keyed_int(interp, sched->foreign_tasks, i);
160 LOCK(PARROT_TASK(task)->waiters_lock);
161 if (PARROT_TASK(task)->killed) {
162 VTABLE_delete_keyed_int(interp, sched->foreign_tasks, i);
163 i--;
164 foreign_count--;
165 }
166 UNLOCK(PARROT_TASK(task)->waiters_lock);
167 }
168
169 /* If we have no scheduled tasks, but we do have an alarm or foreign
170 task, we can wait for one of those before we start executing things
171 again. */
172 alarm_count = VTABLE_get_integer(interp, sched->alarms);
173 if (VTABLE_get_integer(interp, scheduler) == 0 && (alarm_count > 0 || foreign_count > 0)) {
174 /* Nothing to do except to wait for the next alarm to expire */
175 Parrot_thread_wait_for_notification(interp);
176 Parrot_cx_check_alarms(interp, interp->scheduler);
177 }
178 } while (alarm_count || foreign_count || VTABLE_get_integer(interp, scheduler) > 0);
179 }
180
181 /*
182
183 =item C<void Parrot_cx_set_scheduler_alarm(PARROT_INTERP)>
184
185 Set the task switch alarm for the scheduler.
186
187 =cut
188
189 */
190
191 void
Parrot_cx_set_scheduler_alarm(PARROT_INTERP)192 Parrot_cx_set_scheduler_alarm(PARROT_INTERP)
193 {
194 ASSERT_ARGS(Parrot_cx_set_scheduler_alarm)
195 const FLOATVAL time_now = Parrot_floatval_time();
196
197 interp->quantum_done = time_now + PARROT_TASK_SWITCH_QUANTUM;
198 Parrot_alarm_set(interp->quantum_done);
199 }
200
201 /*
202
203 =item C<void Parrot_cx_next_task(PARROT_INTERP, PMC *scheduler)>
204
205 Run the task at the head of the task queue until it ends or is
206 pre-empted.
207
208 =cut
209
210 */
211
212 void
Parrot_cx_next_task(PARROT_INTERP,ARGIN (PMC * scheduler))213 Parrot_cx_next_task(PARROT_INTERP, ARGIN(PMC *scheduler))
214 {
215 ASSERT_ARGS(Parrot_cx_next_task)
216 PMC * const task = VTABLE_shift_pmc(interp, scheduler);
217
218 interp->cur_task = task;
219
220 if (!VTABLE_isa(interp, task, CONST_STRING(interp, "Task")))
221 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
222 "Found a non-Task in the task queue");
223
224 /* If we have no tasks in the queue, we can disable task preemption and
225 save ourselves a few cycles. */
226 if (VTABLE_get_integer(interp, scheduler) > 0)
227 Parrot_cx_enable_preemption(interp);
228 else
229 Parrot_cx_disable_preemption(interp);
230
231 Parrot_ext_call(interp, task, "->");
232 }
233
234 /*
235
236 =item C<opcode_t* Parrot_cx_check_scheduler(PARROT_INTERP, opcode_t *next)>
237
238 Does the scheduler need to wake up and do anything? If so, do that now.
239
240 =cut
241
242 */
243
244 PARROT_CAN_RETURN_NULL
245 opcode_t*
Parrot_cx_check_scheduler(PARROT_INTERP,ARGIN (opcode_t * next))246 Parrot_cx_check_scheduler(PARROT_INTERP, ARGIN(opcode_t *next))
247 {
248 ASSERT_ARGS(Parrot_cx_check_scheduler)
249 PMC * const scheduler = interp->scheduler;
250
251 /* If we have any outstanding alarms, or if we have been requested to
252 wake up, run the scheduler. */
253 if (Parrot_alarm_check(&(interp->last_alarm))
254 || SCHEDULER_wake_requested_TEST(scheduler)) {
255 SCHEDULER_wake_requested_CLEAR(scheduler);
256 return Parrot_cx_run_scheduler(interp, scheduler, next);
257 }
258
259 return next;
260 }
261
262 /*
263
264 =item C<opcode_t* Parrot_cx_run_scheduler(PARROT_INTERP, PMC *scheduler,
265 opcode_t *next)>
266
267 Checks to see if any tasks need to be scheduled or if the current task
268 needs to be pre-empted.
269
270 =cut
271
272 */
273
274 PARROT_EXPORT
275 PARROT_CANNOT_RETURN_NULL
276 opcode_t*
Parrot_cx_run_scheduler(PARROT_INTERP,ARGIN (PMC * scheduler),ARGIN (opcode_t * next))277 Parrot_cx_run_scheduler(PARROT_INTERP, ARGIN(PMC *scheduler), ARGIN(opcode_t *next))
278 {
279 ASSERT_ARGS(Parrot_cx_run_scheduler)
280
281 Parrot_cx_check_alarms(interp, scheduler);
282 Parrot_cx_check_quantum(interp, scheduler);
283
284 if (SCHEDULER_resched_requested_TEST(scheduler)) {
285 SCHEDULER_resched_requested_CLEAR(scheduler);
286
287 /* A task switch will only work in the outer runloop of a fully
288 booted Parrot. In a Parrot that hasn't called begin_execution,
289 or in a nested runloop, we silently ignore task switches. */
290 if (SCHEDULER_enable_scheduler_TEST(scheduler) && interp->current_runloop_level <= 1)
291 return Parrot_cx_preempt_task(interp, scheduler, next);
292 }
293
294 /* Some alarm seems to have fired, but not the scheduler's.
295 * Re-set the scheduler alarm */
296 if (Parrot_cx_preemption_enabled(interp))
297 Parrot_alarm_set(interp->quantum_done);
298
299 return next;
300 }
301
302 /*
303
304 =item C<void Parrot_cx_check_quantum(PARROT_INTERP, PMC *scheduler)>
305
306 If the quantum has expired, schedule the next task.
307
308 =cut
309
310 */
311
312 void
Parrot_cx_check_quantum(PARROT_INTERP,ARGIN (PMC * scheduler))313 Parrot_cx_check_quantum(PARROT_INTERP, ARGIN(PMC *scheduler))
314 {
315 ASSERT_ARGS(Parrot_cx_check_quantum)
316
317 /* If we are using preemption, check the current time and possibly
318 schedule the next preemption */
319 if (Parrot_cx_preemption_enabled(interp)) {
320 const FLOATVAL time_now = Parrot_floatval_time();
321 if (time_now >= interp->quantum_done)
322 SCHEDULER_resched_requested_SET(scheduler);
323 }
324 }
325
326 /*
327
328 =item C<PMC* Parrot_cx_stop_task(PARROT_INTERP, opcode_t *next)>
329
330 Stop the current task and pack it up into a PMC what can be used to resume later.
331
332 =cut
333
334 */
335
336 PARROT_EXPORT
337 PARROT_CANNOT_RETURN_NULL
338 PMC*
Parrot_cx_stop_task(PARROT_INTERP,ARGIN (opcode_t * next))339 Parrot_cx_stop_task(PARROT_INTERP, ARGIN(opcode_t *next))
340 {
341 ASSERT_ARGS(Parrot_cx_stop_task)
342 PMC * const task = Parrot_cx_current_task(interp);
343 Parrot_Task_attributes * const tdata = PARROT_TASK(task);
344 PMC * const cont = Parrot_pmc_new(interp, enum_class_Continuation);
345
346 VTABLE_set_pointer(interp, cont, next);
347
348 /* TODO: This check seems expensive. Do we need to have this active at all
349 times, or can we make this conditional on NDEBUG? */
350 if (PMC_IS_NULL(task) || !VTABLE_isa(interp, task, CONST_STRING(interp, "Task")))
351 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
352 "Attempt to stop invalid interp->current_task");
353
354 tdata->code = cont;
355 PARROT_GC_WRITE_BARRIER(interp, task);
356 TASK_in_preempt_SET(task);
357
358 return task;
359 }
360
361 /*
362
363 =item C<opcode_t* Parrot_cx_preempt_task(PARROT_INTERP, PMC *scheduler, opcode_t
364 *next)>
365
366 Pre-empt the current task. It goes on the foot of the task queue,
367 and then we jump all the way back to the task scheduling loop.
368
369 =cut
370
371 */
372
373 PARROT_CAN_RETURN_NULL
374 opcode_t*
Parrot_cx_preempt_task(PARROT_INTERP,ARGIN (PMC * scheduler),ARGIN (opcode_t * next))375 Parrot_cx_preempt_task(PARROT_INTERP, ARGIN(PMC *scheduler), ARGIN(opcode_t *next))
376 {
377 ASSERT_ARGS(Parrot_cx_preempt_task)
378 PMC * const task = Parrot_cx_stop_task(interp, next);
379 VTABLE_push_pmc(interp, scheduler, task);
380
381 return (opcode_t*)NULL;
382 }
383
384 /*
385
386 =item C<void Parrot_cx_runloop_wake(PARROT_INTERP, PMC *scheduler)>
387
388 Wake a sleeping scheduler runloop (generally called when new tasks are added to
389 the scheduler's task list).
390
391 =cut
392
393 */
394
395 void
Parrot_cx_runloop_wake(PARROT_INTERP,ARGIN (PMC * scheduler))396 Parrot_cx_runloop_wake(PARROT_INTERP, ARGIN(PMC *scheduler))
397 {
398 ASSERT_ARGS(Parrot_cx_runloop_wake)
399 Parrot_runcore_enable_event_checking(interp);
400 SCHEDULER_wake_requested_SET(scheduler);
401 }
402
403
404 /*
405
406 =item C<void Parrot_cx_schedule_task(PARROT_INTERP, PMC *task_or_sub)>
407
408 Add a task to to the task queue for execution.
409
410 Probably cannot be called across interpreters/threads, must instead be
411 called from within the interpreter's runloop.
412
413 =cut
414
415 */
416
417 PARROT_EXPORT
418 void
Parrot_cx_schedule_task(PARROT_INTERP,ARGIN (PMC * task_or_sub))419 Parrot_cx_schedule_task(PARROT_INTERP, ARGIN(PMC *task_or_sub))
420 {
421 ASSERT_ARGS(Parrot_cx_schedule_task)
422 PMC * task = PMCNULL;
423 int index;
424
425 if (!interp->scheduler)
426 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
427 "Scheduler was not initialized for this interpreter");
428
429 /* TODO: Can we do anything less expensive than an ISA check here? */
430 if (VTABLE_isa(interp, task_or_sub, CONST_STRING(interp, "Task")))
431 task = task_or_sub;
432 else if (VTABLE_isa(interp, task_or_sub, CONST_STRING(interp, "Sub"))) {
433 Parrot_Task_attributes *tdata;
434 task = Parrot_pmc_new(interp, enum_class_Task);
435 tdata = PARROT_TASK(task);
436 tdata->code = task_or_sub;
437 PARROT_GC_WRITE_BARRIER(interp, task);
438 }
439 else
440 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
441 "Can only schedule Tasks and Subs");
442
443 #ifdef PARROT_HAS_THREADS
444 /* Search for a thread that is free. If we have a free thread, schedule
445 the task there. Otherwise, find the thread with the fewest tasks in its
446 queue and schedule it there. */
447 index = Parrot_thread_get_free_threads_array_index(NULL);
448 if (index > -1) { /* start a new thread */
449 PMC * const thread = Parrot_thread_create(interp,
450 enum_class_ParrotInterpreter,
451 PARROT_CLONE_DEFAULT);
452 Interp * const thread_interp = (Interp *)VTABLE_get_pointer(interp, thread);
453 Parrot_thread_schedule_task(interp, thread_interp, task);
454 Parrot_thread_insert_thread(interp, thread_interp, index);
455 Parrot_thread_run(interp, thread, task, NULL);
456 }
457 else {
458 /* find the thread with the fewest tasks */
459 Interp ** const threads_array = Parrot_thread_get_threads_array(interp);
460 int numthreads = Parrot_get_num_threads(interp);
461 Interp * candidate = NULL;
462 int i, min_tasks = INT_MAX;
463
464 for (i = 1; i < numthreads; i++)
465 if (threads_array[i]) {
466 int const tasks = VTABLE_get_integer(threads_array[i], threads_array[i]->scheduler);
467 if (tasks < min_tasks) {
468 min_tasks = tasks;
469 candidate = threads_array[i];
470 }
471 }
472 if (candidate == NULL)
473 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
474 "Could not find a free thread");
475
476 Parrot_thread_schedule_task(interp, candidate, task);
477 Parrot_thread_notify_thread(candidate);
478
479 /* going from single to multi tasking? */
480 if (VTABLE_get_integer(interp, interp->scheduler) == 1)
481 Parrot_cx_enable_preemption(interp);
482 }
483 #else
484 /* If we don't have threads, we still have tasks and basic preemption. Add
485 the task to the queue. */
486 VTABLE_push_pmc(interp, interp->scheduler, task);
487
488 /* going from single to multi tasking? */
489 if (VTABLE_get_integer(interp, interp->scheduler) == 1)
490 Parrot_cx_enable_preemption(interp);
491 #endif
492 }
493
494 /*
495
496 =item C<void Parrot_cx_schedule_immediate(PARROT_INTERP, PMC *task_or_sub)>
497
498 Add a task to the task queue for immediate execution.
499
500 =cut
501
502 */
503
504 PARROT_EXPORT
505 void
Parrot_cx_schedule_immediate(PARROT_INTERP,ARGIN (PMC * task_or_sub))506 Parrot_cx_schedule_immediate(PARROT_INTERP, ARGIN(PMC *task_or_sub))
507 {
508 ASSERT_ARGS(Parrot_cx_schedule_immediate)
509 PMC *task;
510
511 /* TODO: Can we do something less expensive than ISA? */
512 if (VTABLE_isa(interp, task_or_sub, CONST_STRING(interp, "Task")))
513 task = task_or_sub;
514 else if (VTABLE_isa(interp, task_or_sub, CONST_STRING(interp, "Sub"))) {
515 Parrot_Task_attributes *tdata;
516 task = Parrot_pmc_new(interp, enum_class_Task);
517 tdata = PARROT_TASK(task);
518 tdata->code = task_or_sub;
519 PARROT_GC_WRITE_BARRIER(interp, task);
520 }
521 else
522 Parrot_ex_throw_from_c_noargs(interp, EXCEPTION_INVALID_OPERATION,
523 "Can only schedule Tasks and Subs");
524
525 VTABLE_unshift_pmc(interp, interp->scheduler, task);
526 SCHEDULER_wake_requested_SET(interp->scheduler);
527 SCHEDULER_resched_requested_SET(interp->scheduler);
528 Parrot_thread_notify_thread(interp);
529 }
530
531 /*
532
533 =item C<PMC* Parrot_cx_current_task(PARROT_INTERP)>
534
535 Returns the task that is currently running.
536
537 =cut
538
539 */
540
541 PARROT_CANNOT_RETURN_NULL
542 PARROT_PURE_FUNCTION
543 PMC*
Parrot_cx_current_task(PARROT_INTERP)544 Parrot_cx_current_task(PARROT_INTERP)
545 {
546 ASSERT_ARGS(Parrot_cx_current_task)
547 return interp->cur_task;
548 }
549
550 /*
551
552 =back
553
554 =head2 Scheduler Message Interface Functions
555
556 Functions that are used to interface with the message queue in the concurrency
557 scheduler.
558
559 =over 4
560
561 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
562 *payload)>
563
564 Send a message to a scheduler in a different interpreter/thread.
565
566 =cut
567
568 */
569
570 PARROT_EXPORT
571 void
Parrot_cx_send_message(PARROT_INTERP,ARGIN (STRING * messagetype),ARGIN (SHIM (PMC * payload)))572 Parrot_cx_send_message(PARROT_INTERP, ARGIN(STRING *messagetype), ARGIN(SHIM(PMC *payload)))
573 {
574 ASSERT_ARGS(Parrot_cx_send_message)
575 if (interp->scheduler) {
576 Parrot_Scheduler_attributes * const sched_struct =
577 PARROT_SCHEDULER(interp->scheduler);
578 PMC * const message = Parrot_pmc_new(interp, enum_class_SchedulerMessage);
579 VTABLE_set_string_native(interp, message, messagetype);
580
581 VTABLE_push_pmc(interp, sched_struct->messages, message);
582 Parrot_cx_runloop_wake(interp, interp->scheduler);
583 }
584 }
585
586 /*
587
588 =item C<void Parrot_cx_schedule_alarm(PARROT_INTERP, PMC *alarm)>
589
590 Schedule an alarm.
591
592 =cut
593
594 */
595
596 void
Parrot_cx_schedule_alarm(PARROT_INTERP,ARGIN (PMC * alarm))597 Parrot_cx_schedule_alarm(PARROT_INTERP, ARGIN(PMC *alarm))
598 {
599 ASSERT_ARGS(Parrot_cx_schedule_alarm)
600 Parrot_Scheduler_attributes * const sched = PARROT_SCHEDULER(interp->scheduler);
601 FLOATVAL alarm_time = VTABLE_get_number(interp, alarm);
602
603 Parrot_alarm_set(alarm_time);
604
605 /* Insert new alarm at correct (ordered by time) position in array. */
606 Parrot_pmc_list_insert_by_number(interp, sched->alarms, alarm);
607 }
608
609 /*
610
611 =item C<void Parrot_cx_check_alarms(PARROT_INTERP, PMC *scheduler)>
612
613 Add the subs attached to any expired alarms to the task queue.
614
615 =cut
616
617 */
618
619 PARROT_EXPORT
620 void
Parrot_cx_check_alarms(PARROT_INTERP,ARGIN (PMC * scheduler))621 Parrot_cx_check_alarms(PARROT_INTERP, ARGIN(PMC *scheduler))
622 {
623 ASSERT_ARGS(Parrot_cx_check_alarms)
624 Parrot_Scheduler_attributes * const sched = PARROT_SCHEDULER(scheduler);
625 INTVAL alarm_count = VTABLE_get_integer(interp, sched->alarms);
626 const FLOATVAL now_time = Parrot_floatval_time();
627
628 /* Loop over all alarms, searching for expired ones. Since they are ordered
629 by execution time, as soon as we find one that is not expired we can
630 exit the loop. For each alarm that is expired, add the associated
631 Sub/Task to the queue. */
632 while (alarm_count) {
633 PMC * const alarm = VTABLE_shift_pmc(interp, sched->alarms);
634 const FLOATVAL alarm_time = VTABLE_get_number(interp, alarm);
635
636 if (alarm_time < now_time) {
637 Parrot_Alarm_attributes * const data = PARROT_ALARM(alarm);
638 Parrot_cx_schedule_immediate(interp, data->alarm_task);
639 }
640 else {
641 Parrot_alarm_set(alarm_time);
642 VTABLE_unshift_pmc(interp, sched->alarms, alarm);
643 break;
644 }
645
646 alarm_count--;
647 }
648 }
649
650 /*
651
652 =back
653
654 =head2 Opcode Functions
655
656 Functions that are called from within opcodes, that take and return an
657 opcode_t* to allow for changing the code flow.
658
659 =over 4
660
661 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
662 opcode_t *next)>
663
664 Add a sleep timer to the scheduler. This function is called by the C<sleep>
665 opcode.
666
667 =cut
668
669 */
670
671 PARROT_EXPORT
672 PARROT_WARN_UNUSED_RESULT
673 PARROT_CAN_RETURN_NULL
674 opcode_t *
Parrot_cx_schedule_sleep(PARROT_INTERP,FLOATVAL time,ARGIN_NULLOK (opcode_t * next))675 Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time, ARGIN_NULLOK(opcode_t *next))
676 {
677 ASSERT_ARGS(Parrot_cx_schedule_sleep)
678 const FLOATVAL now_time = Parrot_floatval_time();
679 const FLOATVAL done_time = now_time + time;
680 PMC * const alarm = Parrot_pmc_new(interp, enum_class_Alarm);
681 Parrot_Alarm_attributes * const adata = PARROT_ALARM(alarm);
682 PMC * const task = Parrot_cx_stop_task(interp, next);
683
684 adata->alarm_time = done_time;
685 PARROT_ASSERT_INTERP(task, interp);
686 adata->alarm_task = task;
687 PARROT_GC_WRITE_BARRIER(interp, alarm);
688 (void) VTABLE_invoke(interp, alarm, NULL);
689
690 return (opcode_t*) NULL;
691 }
692
693 /*
694
695 =back
696
697 =head2 Internal functions
698
699 Functions that are used by the scheduler itself.
700
701 =over 4
702
703 =item C<void Parrot_cx_enable_preemption(PARROT_INTERP)>
704
705 Enable preemption. Used when more than one task is runnable.
706
707 =cut
708
709 */
710
711 void
Parrot_cx_enable_preemption(PARROT_INTERP)712 Parrot_cx_enable_preemption(PARROT_INTERP)
713 {
714 ASSERT_ARGS(Parrot_cx_enable_preemption)
715
716 PMC * const scheduler = interp->scheduler;
717 SCHEDULER_enable_preemption_SET(scheduler);
718 Parrot_cx_set_scheduler_alarm(interp);
719 }
720
721 /*
722
723 =item C<void Parrot_cx_disable_preemption(PARROT_INTERP)>
724
725 Disable preemption. Used when only one task is runnable.
726
727 =cut
728
729 */
730
731 void
Parrot_cx_disable_preemption(PARROT_INTERP)732 Parrot_cx_disable_preemption(PARROT_INTERP)
733 {
734 ASSERT_ARGS(Parrot_cx_disable_preemption)
735
736 PMC * const scheduler = interp->scheduler;
737 SCHEDULER_enable_preemption_CLEAR(scheduler);
738 }
739
740 /*
741
742 =item C<static int Parrot_cx_preemption_enabled(PARROT_INTERP)>
743
744 Checks wether preemption is enabled or not.
745
746 =cut
747
748 */
749
750 static int
Parrot_cx_preemption_enabled(PARROT_INTERP)751 Parrot_cx_preemption_enabled(PARROT_INTERP)
752 {
753 ASSERT_ARGS(Parrot_cx_preemption_enabled)
754
755 PMC * const scheduler = interp->scheduler;
756 return SCHEDULER_enable_preemption_TEST(scheduler);
757 }
758
759 /*
760
761 =back
762
763 =head1 SEE ALSO
764
765 F<include/parrot/scheduler.h>
766
767 =cut
768
769 */
770
771
772 /*
773 * Local variables:
774 * c-file-style: "parrot"
775 * End:
776 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
777 */
778