fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / scheduler.c
blob02d437ee9573607489f3c7b7b0c2278de837d38b
1 /*
2 Copyright (C) 2007-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/scheduler.c - The core routines for the concurrency scheduler
9 =head1 DESCRIPTION
11 Each interpreter has a concurrency scheduler element in its core struct. The
12 scheduler is responsible for receiveing, dispatching, and monitoring events,
13 exceptions, async I/O, and concurrent tasks (threads).
15 =cut
19 #include "parrot/parrot.h"
20 #include "parrot/extend.h"
21 #include "parrot/scheduler_private.h"
22 #include "parrot/runcore_api.h"
24 #include "pmc/pmc_scheduler.h"
25 #include "pmc/pmc_task.h"
26 #include "pmc/pmc_timer.h"
28 #include "scheduler.str"
30 #define CX_DEBUG 0
32 /* HEADERIZER HFILE: include/parrot/scheduler.h */
34 /* HEADERIZER BEGIN: static */
35 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
37 static void scheduler_process_messages(PARROT_INTERP,
38 ARGMOD(PMC *scheduler))
39 __attribute__nonnull__(1)
40 __attribute__nonnull__(2)
41 FUNC_MODIFIES(*scheduler);
43 static void scheduler_process_wait_list(PARROT_INTERP,
44 ARGMOD(PMC *scheduler))
45 __attribute__nonnull__(1)
46 __attribute__nonnull__(2)
47 FUNC_MODIFIES(*scheduler);
49 #define ASSERT_ARGS_scheduler_process_messages __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
50 PARROT_ASSERT_ARG(interp) \
51 , PARROT_ASSERT_ARG(scheduler))
52 #define ASSERT_ARGS_scheduler_process_wait_list __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
53 PARROT_ASSERT_ARG(interp) \
54 , PARROT_ASSERT_ARG(scheduler))
55 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
56 /* HEADERIZER END: static */
60 =head2 Scheduler Interface Functions
62 Functions to interface with the concurrency scheduler.
64 =over 4
66 =item C<void Parrot_cx_init_scheduler(PARROT_INTERP)>
68 Initalize the concurrency scheduler for the interpreter.
70 =cut
74 void
75 Parrot_cx_init_scheduler(PARROT_INTERP)
77 ASSERT_ARGS(Parrot_cx_init_scheduler)
78 if (!interp->parent_interpreter) {
79 PMC *scheduler;
81 /* Add the very first interpreter to the list of interps. */
82 pt_add_to_interpreters(interp, NULL);
84 scheduler = Parrot_pmc_new(interp, enum_class_Scheduler);
85 scheduler = VTABLE_share_ro(interp, scheduler);
87 interp->scheduler = scheduler;
93 =item C<void Parrot_cx_check_tasks(PARROT_INTERP, PMC *scheduler)>
95 If a wake request has been received, handle tasks.
97 =cut
101 void
102 Parrot_cx_check_tasks(PARROT_INTERP, ARGMOD(PMC *scheduler))
104 ASSERT_ARGS(Parrot_cx_check_tasks)
105 if (SCHEDULER_wake_requested_TEST(scheduler))
106 Parrot_cx_handle_tasks(interp, interp->scheduler);
111 =item C<void Parrot_cx_handle_tasks(PARROT_INTERP, PMC *scheduler)>
113 Handle the pending tasks in the scheduler's task list. Returns when there are
114 no more pending tasks.
116 =cut
120 PARROT_EXPORT
121 void
122 Parrot_cx_handle_tasks(PARROT_INTERP, ARGMOD(PMC *scheduler))
124 ASSERT_ARGS(Parrot_cx_handle_tasks)
126 /* avoid recursive calls */
127 if (SCHEDULER_in_handler_TEST(scheduler))
128 return;
130 SCHEDULER_in_handler_SET(scheduler);
131 SCHEDULER_wake_requested_CLEAR(scheduler);
132 Parrot_cx_refresh_task_list(interp, scheduler);
134 while (VTABLE_get_integer(interp, scheduler) > 0) {
135 PMC * const task = VTABLE_pop_pmc(interp, scheduler);
136 if (!PMC_IS_NULL(task)) {
137 PMC * const type_pmc = VTABLE_get_attr_str(interp, task, CONST_STRING(interp, "type"));
138 STRING * const type = VTABLE_get_string(interp, type_pmc);
140 if (Parrot_str_equal(interp, type, CONST_STRING(interp, "callback"))) {
141 Parrot_cx_invoke_callback(interp, task);
143 else if (Parrot_str_equal(interp, type, CONST_STRING(interp, "timer"))) {
144 Parrot_cx_timer_invoke(interp, task);
146 else if (Parrot_str_equal(interp, type, CONST_STRING(interp, "event"))) {
147 PMC * const handler = Parrot_cx_find_handler_for_task(interp, task);
148 if (!PMC_IS_NULL(handler)) {
149 PMC * const handler_sub = VTABLE_get_attr_str(interp, handler, CONST_STRING(interp, "code"));
150 Parrot_ext_call(interp, handler_sub, "PP->", handler, task);
153 else {
154 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
155 "Unknown task type '%Ss'.\n", type);
158 Parrot_cx_delete_task(interp, task);
161 /* If the scheduler was flagged to terminate, make sure you process all
162 * tasks. */
163 if (SCHEDULER_terminate_requested_TEST(scheduler))
164 Parrot_cx_refresh_task_list(interp, scheduler);
166 } /* end of pending tasks */
168 SCHEDULER_in_handler_CLEAR(scheduler);
173 =item C<void Parrot_cx_refresh_task_list(PARROT_INTERP, PMC *scheduler)>
175 Tell the scheduler to perform maintenance on its list of active tasks, checking
176 for completed timers or sleep events, sorting for priority, checking for
177 messages, etc.
179 =cut
183 void
184 Parrot_cx_refresh_task_list(PARROT_INTERP, ARGMOD(PMC *scheduler))
186 ASSERT_ARGS(Parrot_cx_refresh_task_list)
187 scheduler_process_wait_list(interp, scheduler);
188 scheduler_process_messages(interp, scheduler);
190 /* TODO: Sort the task list index */
192 SCHEDULER_cache_valid_SET(scheduler);
193 return;
198 =item C<void Parrot_cx_runloop_wake(PARROT_INTERP, PMC *scheduler)>
200 Wake a sleeping scheduler runloop (generally called when new tasks are added to
201 the scheduler's task list).
203 =cut
207 void
208 Parrot_cx_runloop_wake(PARROT_INTERP, ARGMOD(PMC *scheduler))
210 ASSERT_ARGS(Parrot_cx_runloop_wake)
211 enable_event_checking(interp);
212 SCHEDULER_wake_requested_SET(scheduler);
218 =item C<void Parrot_cx_runloop_end(PARROT_INTERP)>
220 Schedule an event to terminate the scheduler runloop.
222 =cut
226 PARROT_EXPORT
227 void
228 Parrot_cx_runloop_end(PARROT_INTERP)
230 ASSERT_ARGS(Parrot_cx_runloop_end)
231 SCHEDULER_terminate_requested_SET(interp->scheduler);
232 Parrot_cx_handle_tasks(interp, interp->scheduler);
237 =item C<void Parrot_cx_schedule_task(PARROT_INTERP, PMC *task)>
239 Add a task to scheduler's task list. Cannot be called across
240 interpreters/threads, must be called from within the interpreter's runloop.
242 =cut
246 PARROT_EXPORT
247 void
248 Parrot_cx_schedule_task(PARROT_INTERP, ARGIN(PMC *task))
250 ASSERT_ARGS(Parrot_cx_schedule_task)
251 if (!interp->scheduler)
252 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
253 "Scheduler was not initialized for this interpreter.\n");
255 VTABLE_push_pmc(interp, interp->scheduler, task);
260 =item C<PMC * Parrot_cx_peek_task(PARROT_INTERP)>
262 Retrieve the the top task on the scheduler's task list, but don't remove it
263 from the list.
265 =cut
269 PARROT_EXPORT
270 PARROT_CAN_RETURN_NULL
271 PMC *
272 Parrot_cx_peek_task(PARROT_INTERP)
274 ASSERT_ARGS(Parrot_cx_peek_task)
275 if (!interp->scheduler)
276 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
277 "Scheduler was not initialized for this interpreter.\n");
279 return VTABLE_pop_pmc(interp, interp->scheduler);
284 =item C<void Parrot_cx_schedule_timer(PARROT_INTERP, STRING *type, FLOATVAL
285 duration, FLOATVAL interval, INTVAL repeat, PMC *sub)>
287 Create a new timer event due at C<diff> from now, repeated at C<interval>
288 and running the passed C<sub>.
290 =cut
294 PARROT_EXPORT
295 void
296 Parrot_cx_schedule_timer(PARROT_INTERP,
297 ARGIN_NULLOK(STRING *type), FLOATVAL duration, FLOATVAL interval,
298 INTVAL repeat, ARGIN_NULLOK(PMC *sub))
300 ASSERT_ARGS(Parrot_cx_schedule_timer)
301 PMC * const timer = Parrot_pmc_new(interp, enum_class_Timer);
303 VTABLE_set_number_keyed_int(interp, timer, PARROT_TIMER_NSEC, duration);
304 VTABLE_set_number_keyed_int(interp, timer, PARROT_TIMER_INTERVAL, interval);
305 VTABLE_set_integer_keyed_int(interp, timer, PARROT_TIMER_REPEAT, repeat);
307 if (!PMC_IS_NULL(sub))
308 VTABLE_set_pmc_keyed_int(interp, timer, PARROT_TIMER_HANDLER, sub);
310 if (!STRING_IS_NULL(type))
311 VTABLE_set_string_native(interp, timer, type);
313 if (repeat && FLOAT_IS_ZERO(interval))
314 VTABLE_set_number_keyed_int(interp, timer, PARROT_TIMER_INTERVAL, duration);
316 Parrot_cx_schedule_task(interp, timer);
321 =item C<void Parrot_cx_schedule_repeat(PARROT_INTERP, PMC *task)>
323 Add a repeat task to scheduler's task list.
325 =cut
329 PARROT_EXPORT
330 void
331 Parrot_cx_schedule_repeat(PARROT_INTERP, ARGIN(PMC *task))
333 ASSERT_ARGS(Parrot_cx_schedule_repeat)
334 INTVAL repeat = VTABLE_get_integer_keyed_int(interp, task,
335 PARROT_TIMER_REPEAT);
336 FLOATVAL duration = VTABLE_get_number_keyed_int(interp, task,
337 PARROT_TIMER_INTERVAL);
338 if (repeat != 0) {
339 PMC * const repeat_task = VTABLE_clone(interp, task);
340 VTABLE_set_number_keyed_int(interp, repeat_task, PARROT_TIMER_NSEC, duration);
342 if (repeat > 0)
343 VTABLE_set_integer_keyed_int(interp, repeat_task,
344 PARROT_TIMER_REPEAT, repeat - 1);
346 Parrot_cx_schedule_task(interp, repeat_task);
352 =item C<void Parrot_cx_schedule_callback(PARROT_INTERP, PMC *user_data, char
353 *ext_data)>
355 Create a new callback event, with an argument for the call.
357 =cut
361 PARROT_EXPORT
362 void
363 Parrot_cx_schedule_callback(PARROT_INTERP,
364 ARGIN(PMC *user_data), ARGIN(char *ext_data))
366 ASSERT_ARGS(Parrot_cx_schedule_callback)
367 PMC * const callback = Parrot_pmc_new(interp, enum_class_Task);
368 Parrot_Task_attributes * const task_struct = PARROT_TASK(callback);
370 task_struct->type = CONST_STRING(interp, "callback");
371 task_struct->data = user_data;
372 task_struct->cb_data = ext_data;
374 Parrot_cx_schedule_task(interp, callback);
379 =item C<void Parrot_cx_request_suspend_for_gc(PARROT_INTERP)>
381 Tell the scheduler to suspend for GC at the next safe pause.
383 =cut
387 PARROT_EXPORT
388 void
389 Parrot_cx_request_suspend_for_gc(PARROT_INTERP)
391 ASSERT_ARGS(Parrot_cx_request_suspend_for_gc)
392 #if CX_DEBUG
393 fprintf(stderr, "requesting gc suspend [interp=%p]\n", interp);
394 #endif
395 Parrot_cx_send_message(interp, CONST_STRING(interp, "suspend_for_gc"), PMCNULL);
400 =item C<void Parrot_cx_delete_task(PARROT_INTERP, PMC *task)>
402 Remove a task from the scheduler's task list.
404 =cut
408 PARROT_EXPORT
409 void
410 Parrot_cx_delete_task(PARROT_INTERP, ARGIN(PMC *task))
412 ASSERT_ARGS(Parrot_cx_delete_task)
413 if (interp->scheduler) {
414 const INTVAL tid = VTABLE_get_integer(interp, task);
415 VTABLE_delete_keyed_int(interp, interp->scheduler, tid);
417 else if (interp->scheduler)
418 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
419 "Scheduler was not initialized for this interpreter.\n");
425 =item C<PMC * Parrot_cx_delete_suspend_for_gc(PARROT_INTERP)>
427 Remove a message that would suspend GC from the message queue. (Provided for
428 backward compatibility in the threads implementation.)
430 =cut
434 PARROT_EXPORT
435 PARROT_CAN_RETURN_NULL
436 PMC *
437 Parrot_cx_delete_suspend_for_gc(PARROT_INTERP)
439 ASSERT_ARGS(Parrot_cx_delete_suspend_for_gc)
440 if (interp->scheduler) {
441 STRING *suspend_str = CONST_STRING(interp, "suspend_for_gc");
442 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(interp->scheduler);
443 INTVAL num_tasks, index;
445 #if CX_DEBUG
446 fprintf(stderr, "called delete_suspend_for_gc\n");
447 #endif
449 #if CX_DEBUG
450 fprintf(stderr, "locking msg_lock (delete) [interp=%p]\n", interp);
451 #endif
452 LOCK(sched_struct->msg_lock);
453 /* Search the task index for GC suspend tasks */
454 num_tasks = VTABLE_elements(interp, sched_struct->messages);
455 for (index = 0; index < num_tasks; ++index) {
456 PMC *message = VTABLE_get_pmc_keyed_int(interp, sched_struct->messages, index);
457 if (!PMC_IS_NULL(message)
458 && Parrot_str_equal(interp, VTABLE_get_string(interp, message),
459 suspend_str)) {
460 VTABLE_delete_keyed_int(interp, sched_struct->messages, index);
461 UNLOCK(sched_struct->msg_lock);
462 return message;
465 #if CX_DEBUG
466 fprintf(stderr, "unlocking msg_lock (delete) [interp=%p]\n", interp);
467 #endif
468 UNLOCK(sched_struct->msg_lock);
471 else
472 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
473 "Scheduler was not initialized for this interpreter.\n");
475 return PMCNULL;
480 =item C<void Parrot_cx_add_handler_local(PARROT_INTERP, PMC *handler)>
482 Add a handler to the current context's list of handlers.
484 =cut
488 PARROT_EXPORT
489 void
490 Parrot_cx_add_handler_local(PARROT_INTERP, ARGIN(PMC *handler))
492 ASSERT_ARGS(Parrot_cx_add_handler_local)
493 if (PMC_IS_NULL(Parrot_pcc_get_handlers(interp, interp->ctx)))
494 Parrot_pcc_set_handlers(interp, interp->ctx, Parrot_pmc_new(interp,
495 enum_class_ResizablePMCArray));
497 VTABLE_unshift_pmc(interp, Parrot_pcc_get_handlers(interp, interp->ctx), handler);
503 =item C<void Parrot_cx_delete_handler_local(PARROT_INTERP, STRING
504 *handler_type)>
506 Remove the top task handler of a particular type from the context's list of
507 handlers.
509 =cut
513 PARROT_EXPORT
514 void
515 Parrot_cx_delete_handler_local(PARROT_INTERP, ARGIN(STRING *handler_type))
517 ASSERT_ARGS(Parrot_cx_delete_handler_local)
518 PMC *handlers = Parrot_pcc_get_handlers(interp, interp->ctx);
520 if (PMC_IS_NULL(handlers))
521 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
522 "No handler to delete.");
524 if (STRING_IS_NULL(handler_type) || STRING_IS_EMPTY(handler_type))
525 VTABLE_shift_pmc(interp, handlers);
526 else {
527 /* Loop from newest handler to oldest handler. */
528 STRING *exception_str = CONST_STRING(interp, "exception");
529 STRING *event_str = CONST_STRING(interp, "event");
530 STRING *handler_str = CONST_STRING(interp, "ExceptionHandler");
531 const INTVAL elements = VTABLE_elements(interp, handlers);
532 INTVAL index;
533 typedef enum { Hunknown, Hexception, Hevent } Htype;
535 const Htype htype =
536 Parrot_str_equal(interp, handler_type, exception_str) ?
537 Hexception :
538 Parrot_str_equal(interp, handler_type, event_str) ?
539 Hevent :
540 Hunknown;
541 STRING * const handler_name = (htype == Hexception) ? handler_str : (STRING *)NULL;
543 for (index = 0; index < elements; ++index) {
544 PMC * const handler = VTABLE_get_pmc_keyed_int(interp, handlers, index);
545 if (!PMC_IS_NULL(handler)) {
546 switch (htype) {
547 case Hexception:
548 if (VTABLE_isa(interp, handler, handler_name)) {
549 VTABLE_set_pmc_keyed_int(interp, handlers, index, PMCNULL);
550 return;
552 break;
553 case Hevent:
554 if (handler->vtable->base_type == enum_class_EventHandler) {
555 VTABLE_set_pmc_keyed_int(interp, handlers, index, PMCNULL);
556 return;
558 break;
559 default:
560 break;
565 Parrot_ex_throw_from_c_args(interp, NULL,
566 EXCEPTION_INVALID_OPERATION, "No handler to delete.");
573 =item C<INTVAL Parrot_cx_count_handlers_local(PARROT_INTERP, STRING
574 *handler_type)>
576 Count the number of active handlers of a particular type from the
577 context's list of handlers.
579 =cut
583 PARROT_EXPORT
584 INTVAL
585 Parrot_cx_count_handlers_local(PARROT_INTERP, ARGIN(STRING *handler_type))
587 ASSERT_ARGS(Parrot_cx_count_handlers_local)
588 PMC * const handlers = Parrot_pcc_get_handlers(interp, interp->ctx);
589 INTVAL elements;
591 if (PMC_IS_NULL(handlers))
592 return 0;
594 elements = VTABLE_elements(interp, handlers);
596 if (STRING_IS_NULL(handler_type) || STRING_IS_EMPTY(handler_type))
597 return elements;
599 /* Loop from newest handler to oldest handler. */
601 STRING *exception_str = CONST_STRING(interp, "exception");
602 STRING *event_str = CONST_STRING(interp, "event");
603 STRING *handler_str = CONST_STRING(interp, "ExceptionHandler");
604 INTVAL count = 0;
605 INTVAL index;
606 typedef enum { Hunknown, Hexception, Hevent } Htype;
608 const Htype htype =
609 (Parrot_str_equal(interp, handler_type, exception_str)) ?
610 Hexception :
611 (Parrot_str_equal(interp, handler_type, event_str)) ?
612 Hevent :
613 Hunknown;
614 STRING * const handler_name = (htype == Hexception) ? handler_str : (STRING *)NULL;
616 for (index = 0; index < elements; ++index) {
617 PMC * const handler = VTABLE_get_pmc_keyed_int(interp, handlers, index);
618 if (!PMC_IS_NULL(handler)) {
619 switch (htype) {
620 case Hexception:
621 if (VTABLE_isa(interp, handler, handler_name))
622 ++count;
623 break;
624 case Hevent:
625 if (handler->vtable->base_type == enum_class_EventHandler)
626 ++count;
627 break;
628 default:
629 break;
633 return count;
640 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
642 Add a task handler to scheduler's list of handlers.
644 =cut
648 PARROT_EXPORT
649 void
650 Parrot_cx_add_handler(PARROT_INTERP, ARGIN(PMC *handler))
652 ASSERT_ARGS(Parrot_cx_add_handler)
653 STRING * const add_handler = CONST_STRING(interp, "add_handler");
654 if (!interp->scheduler)
655 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
656 "Scheduler was not initialized for this interpreter.\n");
658 Parrot_pcc_invoke_method_from_c_args(interp, interp->scheduler, add_handler, "P->", handler);
663 =item C<void Parrot_cx_delete_handler_typed(PARROT_INTERP, STRING
664 *handler_type)>
666 Remove the top task handler of a particular type from the scheduler's list of
667 handlers.
669 =cut
673 PARROT_EXPORT
674 void
675 Parrot_cx_delete_handler_typed(PARROT_INTERP, ARGIN(STRING *handler_type))
677 ASSERT_ARGS(Parrot_cx_delete_handler_typed)
678 if (!interp->scheduler)
679 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
680 "Scheduler was not initialized for this interpreter.\n");
682 Parrot_pcc_invoke_method_from_c_args(interp, interp->scheduler, CONST_STRING(interp, "delete_handler"), "S->", handler_type);
687 =item C<INTVAL Parrot_cx_count_handlers_typed(PARROT_INTERP, STRING
688 *handler_type)>
690 Count the number of active handlers of a particular type (event, exception) in
691 the concurrency scheduler.
693 =cut
697 PARROT_EXPORT
698 INTVAL
699 Parrot_cx_count_handlers_typed(PARROT_INTERP, ARGIN(STRING *handler_type))
701 ASSERT_ARGS(Parrot_cx_count_handlers_typed)
702 INTVAL count = 0;
704 if (!interp->scheduler)
705 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
706 "Scheduler was not initialized for this interpreter.\n");
708 Parrot_pcc_invoke_method_from_c_args(interp, interp->scheduler, CONST_STRING(interp, "count_handlers"), "S->I", handler_type, &count);
710 return count;
715 =back
717 =head2 Scheduler Message Interface Functions
719 Functions that are used to interface with the message queue in the concurrency
720 scheduler.
722 =over 4
724 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
725 *payload)>
727 Send a message to a scheduler in a different interpreter/thread.
729 =cut
733 PARROT_EXPORT
734 void
735 Parrot_cx_send_message(PARROT_INTERP, ARGIN(STRING *messagetype), SHIM(PMC *payload))
737 ASSERT_ARGS(Parrot_cx_send_message)
738 if (interp->scheduler) {
739 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(interp->scheduler);
740 PMC *message = Parrot_pmc_new(interp, enum_class_SchedulerMessage);
741 VTABLE_set_string_native(interp, message, messagetype);
742 message = VTABLE_share_ro(interp, message);
744 #if CX_DEBUG
745 fprintf(stderr, "sending message[interp=%p]\n", interp);
746 #endif
748 #if CX_DEBUG
749 fprintf(stderr, "locking msg_lock (send) [interp=%p]\n", interp);
750 #endif
751 LOCK(sched_struct->msg_lock);
752 VTABLE_push_pmc(interp, sched_struct->messages, message);
753 #if CX_DEBUG
754 fprintf(stderr, "unlocking msg_lock (send) [interp=%p]\n", interp);
755 #endif
756 UNLOCK(sched_struct->msg_lock);
757 Parrot_cx_runloop_wake(interp, interp->scheduler);
765 =item C<void Parrot_cx_broadcast_message(PARROT_INTERP, STRING *messagetype, PMC
766 *data)>
768 Send a message to the schedulers in all interpreters/threads linked to this
769 one.
771 =cut
775 PARROT_EXPORT
776 void
777 Parrot_cx_broadcast_message(PARROT_INTERP, ARGIN(STRING *messagetype), ARGIN_NULLOK(PMC *data))
779 ASSERT_ARGS(Parrot_cx_broadcast_message)
780 UINTVAL i;
781 LOCK(interpreter_array_mutex);
782 for (i = 0; i < n_interpreters; ++i) {
783 Parrot_Interp other_interp = interpreter_array[i];
784 if (interp == other_interp)
785 continue;
786 Parrot_cx_send_message(other_interp, messagetype, data);
788 UNLOCK(interpreter_array_mutex);
794 =back
796 =head2 Task Interface Functions
798 Functions that are used to interface with a specific task in the concurrency scheduler.
800 =over 4
802 =item C<PMC * Parrot_cx_find_handler_for_task(PARROT_INTERP, PMC *task)>
804 Retrieve a handler appropriate to a given task. If the scheduler has no
805 appropriate handler, returns PMCNULL.
807 =cut
811 PARROT_EXPORT
812 PARROT_CAN_RETURN_NULL
813 PMC *
814 Parrot_cx_find_handler_for_task(PARROT_INTERP, ARGIN(PMC *task))
816 ASSERT_ARGS(Parrot_cx_find_handler_for_task)
817 PMC *handler = PMCNULL;
818 #if CX_DEBUG
819 fprintf(stderr, "searching for handler\n");
820 #endif
822 if (!interp->scheduler)
823 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
824 "Scheduler was not initialized for this interpreter.\n");
826 Parrot_pcc_invoke_method_from_c_args(interp, interp->scheduler, CONST_STRING(interp, "find_handler"), "P->P", task, &handler);
828 #if CX_DEBUG
829 fprintf(stderr, "done searching for handler\n");
830 #endif
832 return handler;
837 =item C<PMC * Parrot_cx_find_handler_local(PARROT_INTERP, PMC *task)>
839 Retrieve a handler appropriate to a given task from the local context. If the
840 context has no appropriate handler, returns PMCNULL.
842 =cut
846 PARROT_EXPORT
847 PARROT_CAN_RETURN_NULL
848 PMC *
849 Parrot_cx_find_handler_local(PARROT_INTERP, ARGIN(PMC *task))
851 ASSERT_ARGS(Parrot_cx_find_handler_local)
854 * Quick&dirty way to avoid infinite recursion
855 * when an exception is thrown while looking
856 * for a handler
858 static int already_doing = 0;
859 static PMC * keep_context = NULL;
861 PMC *context;
862 PMC *iter = PMCNULL;
863 STRING * const handled_str = CONST_STRING(interp, "handled");
864 STRING * const iter_str = CONST_STRING(interp, "handler_iter");
866 if (already_doing) {
867 Parrot_io_eprintf(interp,
868 "** Exception caught while looking for a handler, trying next **\n");
869 if (! keep_context)
870 return NULL;
872 * Note that we are now trying to handle the new exception,
873 * not the initial task argument (exception or whatever).
875 context = Parrot_pcc_get_caller_ctx(interp, keep_context);
876 keep_context = NULL;
877 if (context && !PMC_IS_NULL(Parrot_pcc_get_handlers(interp, context)))
878 iter = VTABLE_get_iter(interp, Parrot_pcc_get_handlers(interp, context));
879 else
880 iter = PMCNULL;
882 else {
883 ++already_doing;
885 /* Exceptions store the handler iterator for rethrow, other kinds of
886 * tasks don't (though they could). */
887 if (task->vtable->base_type == enum_class_Exception
888 && VTABLE_get_integer_keyed_str(interp, task, handled_str) == -1) {
889 iter = VTABLE_get_attr_str(interp, task, iter_str);
890 context = (PMC *)VTABLE_get_pointer(interp, task);
892 else {
893 context = CURRENT_CONTEXT(interp);
894 if (!PMC_IS_NULL(Parrot_pcc_get_handlers(interp, context)))
895 iter = VTABLE_get_iter(interp, Parrot_pcc_get_handlers(interp, context));
899 while (context) {
900 keep_context = context;
901 /* Loop from newest handler to oldest handler. */
902 while (!PMC_IS_NULL(iter) && VTABLE_get_bool(interp, iter)) {
903 PMC * const handler = VTABLE_shift_pmc(interp, iter);
905 if (!PMC_IS_NULL(handler)) {
906 INTVAL valid_handler = 0;
907 if (handler->vtable->base_type == enum_class_Object)
908 Parrot_pcc_invoke_method_from_c_args(interp, handler, CONST_STRING(interp, "can_handle"),
909 "P->I", task, &valid_handler);
910 else
911 Parrot_pcc_invoke_method_from_c_args(interp, handler, CONST_STRING(interp, "can_handle"),
912 "P->I", task, &valid_handler);
914 if (valid_handler) {
915 if (task->vtable->base_type == enum_class_Exception) {
916 /* Store iterator and context for a later rethrow. */
917 VTABLE_set_attr_str(interp, task, CONST_STRING(interp, "handler_iter"), iter);
918 VTABLE_set_pointer(interp, task, context);
920 --already_doing;
921 keep_context = NULL;
922 return handler;
927 /* Continue the search in the next context up the chain. */
928 context = Parrot_pcc_get_caller_ctx(interp, context);
929 if (context && !PMC_IS_NULL(Parrot_pcc_get_handlers(interp, context)))
930 iter = VTABLE_get_iter(interp, Parrot_pcc_get_handlers(interp, context));
931 else
932 iter = PMCNULL;
935 /* Reached the end of the context chain without finding a handler. */
937 --already_doing;
938 return PMCNULL;
943 =item C<void Parrot_cx_timer_invoke(PARROT_INTERP, PMC *timer)>
945 Run the associated code block for a timer event, when the timer fires.
947 =cut
951 void
952 Parrot_cx_timer_invoke(PARROT_INTERP, ARGIN(PMC *timer))
954 ASSERT_ARGS(Parrot_cx_timer_invoke)
955 Parrot_Timer_attributes * const timer_struct = PARROT_TIMER(timer);
956 #if CX_DEBUG
957 fprintf(stderr, "current timer time: %f, %f\n",
958 timer_struct->birthtime + timer_struct->duration,
959 Parrot_floatval_time());
960 #endif
961 if (!PMC_IS_NULL(timer_struct->codeblock)) {
962 Parrot_ext_call(interp, timer_struct->codeblock, "->");
968 =item C<void Parrot_cx_invoke_callback(PARROT_INTERP, PMC *callback)>
970 Run the associated code block for a callback event.
972 =cut
976 void
977 Parrot_cx_invoke_callback(PARROT_INTERP, ARGIN(PMC *callback))
979 ASSERT_ARGS(Parrot_cx_invoke_callback)
980 Parrot_Task_attributes * const task_struct = PARROT_TASK(callback);
981 if (!PMC_IS_NULL(task_struct->data)) {
982 Parrot_run_callback(interp, task_struct->data,
983 task_struct->cb_data);
989 =back
991 =head2 Opcode Functions
993 Functions that are called from within opcodes, that take and return an
994 opcode_t* to allow for changing the code flow.
996 =over 4
999 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
1000 opcode_t *next)>
1002 Add a sleep timer to the scheduler. This function is called by the C<sleep>
1003 opcode.
1005 =cut
1009 PARROT_EXPORT
1010 PARROT_WARN_UNUSED_RESULT
1011 PARROT_CAN_RETURN_NULL
1012 opcode_t *
1013 Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time, ARGIN_NULLOK(opcode_t *next))
1015 ASSERT_ARGS(Parrot_cx_schedule_sleep)
1016 #ifdef PARROT_HAS_THREADS
1017 Parrot_cond condition;
1018 Parrot_mutex lock;
1019 const FLOATVAL timer_end = time + Parrot_floatval_time();
1020 struct timespec time_struct;
1022 /* Tell the scheduler runloop to wake, this is a good time to process
1023 * pending tasks. */
1024 Parrot_cx_runloop_wake(interp, interp->scheduler);
1026 /* Tell this thread to sleep for the requested time. */
1027 COND_INIT(condition);
1028 MUTEX_INIT(lock);
1029 LOCK(lock);
1030 time_struct.tv_sec = (time_t) timer_end;
1031 time_struct.tv_nsec = (long)((timer_end - time_struct.tv_sec)*1000.0f) *1000L*1000L;
1032 COND_TIMED_WAIT(condition, lock, &time_struct);
1033 UNLOCK(lock);
1034 COND_DESTROY(condition);
1035 MUTEX_DESTROY(lock);
1036 #else
1037 /* A more primitive, platform-specific, non-threaded form of sleep. */
1038 if (time > 1000) {
1039 /* prevent integer overflow when converting to microseconds */
1040 const int seconds = floor(time);
1041 Parrot_sleep(seconds);
1042 time -= seconds;
1044 Parrot_usleep((UINTVAL) time*1000000);
1045 #endif
1046 return next;
1052 =back
1054 =head2 Internal Functions
1056 Functions that are only used within the scheduler.
1058 =over 4
1060 =item C<static void scheduler_process_wait_list(PARROT_INTERP, PMC *scheduler)>
1062 Scheduler maintenance, scan the list of waiting tasks to see if any are ready
1063 to become active tasks.
1065 =cut
1069 static void
1070 scheduler_process_wait_list(PARROT_INTERP, ARGMOD(PMC *scheduler))
1072 ASSERT_ARGS(scheduler_process_wait_list)
1073 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(scheduler);
1074 INTVAL num_tasks, index;
1076 /* Sweep the wait list for completed timers */
1077 num_tasks = VTABLE_elements(interp, sched_struct->wait_index);
1078 for (index = 0; index < num_tasks; ++index) {
1079 INTVAL tid = VTABLE_get_integer_keyed_int(interp, sched_struct->wait_index, index);
1080 if (tid > 0) {
1081 PMC *task = VTABLE_get_pmc_keyed_int(interp, sched_struct->task_list, tid);
1082 if (PMC_IS_NULL(task)) {
1083 /* Cleanup expired tasks. */
1084 VTABLE_set_integer_keyed_int(interp, sched_struct->wait_index, index, 0);
1086 else {
1087 /* Move the timer to the active task list if the timer has
1088 * completed. */
1089 FLOATVAL timer_end_time = VTABLE_get_number_keyed_int(interp,
1090 task, PARROT_TIMER_NSEC);
1091 if (timer_end_time <= Parrot_floatval_time()) {
1092 VTABLE_push_integer(interp, sched_struct->task_index, tid);
1093 VTABLE_set_integer_keyed_int(interp, sched_struct->wait_index, index, 0);
1094 Parrot_cx_schedule_repeat(interp, task);
1095 SCHEDULER_cache_valid_CLEAR(scheduler);
1104 =over 4
1106 =item C<static void scheduler_process_messages(PARROT_INTERP, PMC *scheduler)>
1108 Scheduler maintenance, scan the list of messages sent from other schedulers and
1109 take appropriate action on any received.
1111 =cut
1115 static void
1116 scheduler_process_messages(PARROT_INTERP, ARGMOD(PMC *scheduler))
1118 ASSERT_ARGS(scheduler_process_messages)
1119 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(scheduler);
1121 PMC *message;
1122 STRING *suspend_str = CONST_STRING(interp, "suspend_for_gc");
1124 #if CX_DEBUG
1125 fprintf(stderr, "processing messages [interp=%p]\n", interp);
1126 #endif
1128 while (VTABLE_elements(interp, sched_struct->messages) > 0) {
1129 #if CX_DEBUG
1130 fprintf(stderr, "locking msg_lock (process) [interp=%p]\n", interp);
1131 #endif
1132 LOCK(sched_struct->msg_lock);
1133 message = VTABLE_pop_pmc(interp, sched_struct->messages);
1134 #if CX_DEBUG
1135 fprintf(stderr, "unlocking msg_lock (process) [interp=%p]\n", interp);
1136 #endif
1137 UNLOCK(sched_struct->msg_lock);
1138 if (!PMC_IS_NULL(message)
1139 && Parrot_str_equal(interp, VTABLE_get_string(interp, message),
1140 suspend_str)) {
1141 #if CX_DEBUG
1142 fprintf(stderr, "found a suspend, suspending [interp=%p]\n", interp);
1143 #endif
1144 pt_suspend_self_for_gc(interp);
1152 =back
1154 =cut
1160 * Local variables:
1161 * c-file-style: "parrot"
1162 * End:
1163 * vim: expandtab shiftwidth=4: