2 Copyright (C) 2007-2010, Parrot Foundation.
7 src/scheduler.c - The core routines for the concurrency scheduler
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).
19 #include "parrot/parrot.h"
20 #include "parrot/scheduler_private.h"
21 #include "parrot/runcore_api.h"
23 #include "pmc/pmc_scheduler.h"
24 #include "pmc/pmc_task.h"
25 #include "pmc/pmc_timer.h"
27 #include "scheduler.str"
31 /* HEADERIZER HFILE: include/parrot/scheduler.h */
33 /* HEADERIZER BEGIN: static */
34 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
36 static void scheduler_process_messages(PARROT_INTERP
,
37 ARGMOD(PMC
*scheduler
))
38 __attribute__nonnull__(1)
39 __attribute__nonnull__(2)
40 FUNC_MODIFIES(*scheduler
);
42 static void scheduler_process_wait_list(PARROT_INTERP
,
43 ARGMOD(PMC
*scheduler
))
44 __attribute__nonnull__(1)
45 __attribute__nonnull__(2)
46 FUNC_MODIFIES(*scheduler
);
48 #define ASSERT_ARGS_scheduler_process_messages __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
49 PARROT_ASSERT_ARG(interp) \
50 , PARROT_ASSERT_ARG(scheduler))
51 #define ASSERT_ARGS_scheduler_process_wait_list __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
52 PARROT_ASSERT_ARG(interp) \
53 , PARROT_ASSERT_ARG(scheduler))
54 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
55 /* HEADERIZER END: static */
59 =head2 Scheduler Interface Functions
61 Functions to interface with the concurrency scheduler.
65 =item C<void Parrot_cx_init_scheduler(PARROT_INTERP)>
67 Initalize the concurrency scheduler for the interpreter.
74 Parrot_cx_init_scheduler(PARROT_INTERP
)
76 ASSERT_ARGS(Parrot_cx_init_scheduler
)
77 if (!interp
->parent_interpreter
) {
80 /* Add the very first interpreter to the list of interps. */
81 pt_add_to_interpreters(interp
, NULL
);
83 scheduler
= Parrot_pmc_new(interp
, enum_class_Scheduler
);
84 scheduler
= VTABLE_share_ro(interp
, scheduler
);
86 interp
->scheduler
= scheduler
;
92 =item C<void Parrot_cx_check_tasks(PARROT_INTERP, PMC *scheduler)>
94 If a wake request has been received, handle tasks.
101 Parrot_cx_check_tasks(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
103 ASSERT_ARGS(Parrot_cx_check_tasks
)
104 if (SCHEDULER_wake_requested_TEST(scheduler
))
105 Parrot_cx_handle_tasks(interp
, interp
->scheduler
);
110 =item C<void Parrot_cx_handle_tasks(PARROT_INTERP, PMC *scheduler)>
112 Handle the pending tasks in the scheduler's task list. Returns when there are
113 no more pending tasks. Returns 0 to terminate the scheduler runloop, or 1 to
114 continue the runloop.
122 Parrot_cx_handle_tasks(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
124 ASSERT_ARGS(Parrot_cx_handle_tasks
)
125 SCHEDULER_wake_requested_CLEAR(scheduler
);
126 Parrot_cx_refresh_task_list(interp
, scheduler
);
128 while (VTABLE_get_integer(interp
, scheduler
) > 0) {
129 PMC
* const task
= VTABLE_pop_pmc(interp
, scheduler
);
130 if (!PMC_IS_NULL(task
)) {
131 PMC
* const type_pmc
= VTABLE_get_attr_str(interp
, task
, CONST_STRING(interp
, "type"));
132 STRING
* const type
= VTABLE_get_string(interp
, type_pmc
);
134 if (Parrot_str_equal(interp
, type
, CONST_STRING(interp
, "callback"))) {
135 Parrot_cx_invoke_callback(interp
, task
);
137 else if (Parrot_str_equal(interp
, type
, CONST_STRING(interp
, "timer"))) {
138 Parrot_cx_timer_invoke(interp
, task
);
140 else if (Parrot_str_equal(interp
, type
, CONST_STRING(interp
, "event"))) {
141 PMC
* const handler
= Parrot_cx_find_handler_for_task(interp
, task
);
142 if (!PMC_IS_NULL(handler
)) {
143 PMC
* const handler_sub
= VTABLE_get_attr_str(interp
, handler
, CONST_STRING(interp
, "code"));
144 Parrot_pcc_invoke_sub_from_c_args(interp
, handler_sub
,
145 "PP->", handler
, task
);
149 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
150 "Unknown task type '%Ss'.\n", type
);
153 Parrot_cx_delete_task(interp
, task
);
156 /* If the scheduler was flagged to terminate, make sure you process all
158 if (SCHEDULER_terminate_requested_TEST(scheduler
))
159 Parrot_cx_refresh_task_list(interp
, scheduler
);
161 } /* end of pending tasks */
166 =item C<void Parrot_cx_refresh_task_list(PARROT_INTERP, PMC *scheduler)>
168 Tell the scheduler to perform maintenance on its list of active tasks, checking
169 for completed timers or sleep events, sorting for priority, checking for
177 Parrot_cx_refresh_task_list(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
179 ASSERT_ARGS(Parrot_cx_refresh_task_list
)
180 scheduler_process_wait_list(interp
, scheduler
);
181 scheduler_process_messages(interp
, scheduler
);
183 /* TODO: Sort the task list index */
185 SCHEDULER_cache_valid_SET(scheduler
);
191 =item C<void Parrot_cx_runloop_wake(PARROT_INTERP, PMC *scheduler)>
193 Wake a sleeping scheduler runloop (generally called when new tasks are added to
194 the scheduler's task list).
201 Parrot_cx_runloop_wake(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
203 ASSERT_ARGS(Parrot_cx_runloop_wake
)
204 enable_event_checking(interp
);
205 SCHEDULER_wake_requested_SET(scheduler
);
211 =item C<void Parrot_cx_runloop_end(PARROT_INTERP)>
213 Schedule an event to terminate the scheduler runloop.
221 Parrot_cx_runloop_end(PARROT_INTERP
)
223 ASSERT_ARGS(Parrot_cx_runloop_end
)
224 SCHEDULER_terminate_requested_SET(interp
->scheduler
);
225 Parrot_cx_handle_tasks(interp
, interp
->scheduler
);
230 =item C<void Parrot_cx_schedule_task(PARROT_INTERP, PMC *task)>
232 Add a task to scheduler's task list. Cannot be called across
233 interpreters/threads, must be called from within the interpreter's runloop.
241 Parrot_cx_schedule_task(PARROT_INTERP
, ARGIN(PMC
*task
))
243 ASSERT_ARGS(Parrot_cx_schedule_task
)
244 if (!interp
->scheduler
)
245 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
246 "Scheduler was not initialized for this interpreter.\n");
248 VTABLE_push_pmc(interp
, interp
->scheduler
, task
);
253 =item C<PMC * Parrot_cx_peek_task(PARROT_INTERP)>
255 Retrieve the the top task on the scheduler's task list, but don't remove it
263 PARROT_CAN_RETURN_NULL
265 Parrot_cx_peek_task(PARROT_INTERP
)
267 ASSERT_ARGS(Parrot_cx_peek_task
)
268 if (!interp
->scheduler
)
269 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
270 "Scheduler was not initialized for this interpreter.\n");
272 return VTABLE_pop_pmc(interp
, interp
->scheduler
);
277 =item C<void Parrot_cx_schedule_timer(PARROT_INTERP, STRING *type, FLOATVAL
278 duration, FLOATVAL interval, INTVAL repeat, PMC *sub)>
280 Create a new timer event due at C<diff> from now, repeated at C<interval>
281 and running the passed C<sub>.
289 Parrot_cx_schedule_timer(PARROT_INTERP
,
290 ARGIN_NULLOK(STRING
*type
), FLOATVAL duration
, FLOATVAL interval
,
291 INTVAL repeat
, ARGIN_NULLOK(PMC
*sub
))
293 ASSERT_ARGS(Parrot_cx_schedule_timer
)
294 PMC
* const timer
= Parrot_pmc_new(interp
, enum_class_Timer
);
296 VTABLE_set_number_keyed_int(interp
, timer
, PARROT_TIMER_NSEC
, duration
);
297 VTABLE_set_number_keyed_int(interp
, timer
, PARROT_TIMER_INTERVAL
, interval
);
298 VTABLE_set_integer_keyed_int(interp
, timer
, PARROT_TIMER_REPEAT
, repeat
);
300 if (!PMC_IS_NULL(sub
))
301 VTABLE_set_pmc_keyed_int(interp
, timer
, PARROT_TIMER_HANDLER
, sub
);
303 if (!STRING_IS_NULL(type
))
304 VTABLE_set_string_native(interp
, timer
, type
);
306 if (repeat
&& FLOAT_IS_ZERO(interval
))
307 VTABLE_set_number_keyed_int(interp
, timer
, PARROT_TIMER_INTERVAL
, duration
);
309 Parrot_cx_schedule_task(interp
, timer
);
314 =item C<void Parrot_cx_schedule_repeat(PARROT_INTERP, PMC *task)>
316 Add a repeat task to scheduler's task list.
324 Parrot_cx_schedule_repeat(PARROT_INTERP
, ARGIN(PMC
*task
))
326 ASSERT_ARGS(Parrot_cx_schedule_repeat
)
327 INTVAL repeat
= VTABLE_get_integer_keyed_int(interp
, task
,
328 PARROT_TIMER_REPEAT
);
329 FLOATVAL duration
= VTABLE_get_number_keyed_int(interp
, task
,
330 PARROT_TIMER_INTERVAL
);
332 PMC
* const repeat_task
= VTABLE_clone(interp
, task
);
333 VTABLE_set_number_keyed_int(interp
, repeat_task
, PARROT_TIMER_NSEC
, duration
);
336 VTABLE_set_integer_keyed_int(interp
, repeat_task
,
337 PARROT_TIMER_REPEAT
, repeat
- 1);
339 Parrot_cx_schedule_task(interp
, repeat_task
);
345 =item C<void Parrot_cx_schedule_callback(PARROT_INTERP, PMC *user_data, char
348 Create a new callback event, with an argument for the call.
356 Parrot_cx_schedule_callback(PARROT_INTERP
,
357 ARGIN(PMC
*user_data
), ARGIN(char *ext_data
))
359 ASSERT_ARGS(Parrot_cx_schedule_callback
)
360 PMC
* const callback
= Parrot_pmc_new(interp
, enum_class_Task
);
361 Parrot_Task_attributes
* const task_struct
= PARROT_TASK(callback
);
363 task_struct
->type
= CONST_STRING(interp
, "callback");
364 task_struct
->data
= user_data
;
365 task_struct
->cb_data
= ext_data
;
367 Parrot_cx_schedule_task(interp
, callback
);
372 =item C<void Parrot_cx_request_suspend_for_gc(PARROT_INTERP)>
374 Tell the scheduler to suspend for GC at the next safe pause.
382 Parrot_cx_request_suspend_for_gc(PARROT_INTERP
)
384 ASSERT_ARGS(Parrot_cx_request_suspend_for_gc
)
386 fprintf(stderr
, "requesting gc suspend [interp=%p]\n", interp
);
388 Parrot_cx_send_message(interp
, CONST_STRING(interp
, "suspend_for_gc"), PMCNULL
);
393 =item C<void Parrot_cx_delete_task(PARROT_INTERP, PMC *task)>
395 Remove a task from the scheduler's task list.
403 Parrot_cx_delete_task(PARROT_INTERP
, ARGIN(PMC
*task
))
405 ASSERT_ARGS(Parrot_cx_delete_task
)
406 if (interp
->scheduler
&& !PObj_on_free_list_TEST(interp
->scheduler
)) {
407 const INTVAL tid
= VTABLE_get_integer(interp
, task
);
408 VTABLE_delete_keyed_int(interp
, interp
->scheduler
, tid
);
410 else if (interp
->scheduler
)
411 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
412 "Scheduler was not initialized for this interpreter.\n");
418 =item C<PMC * Parrot_cx_delete_suspend_for_gc(PARROT_INTERP)>
420 Remove a message that would suspend GC from the message queue. (Provided for
421 backward compatibility in the threads implementation.)
428 PARROT_CAN_RETURN_NULL
430 Parrot_cx_delete_suspend_for_gc(PARROT_INTERP
)
432 ASSERT_ARGS(Parrot_cx_delete_suspend_for_gc
)
433 if (interp
->scheduler
) {
434 STRING
*suspend_str
= CONST_STRING(interp
, "suspend_for_gc");
435 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(interp
->scheduler
);
436 INTVAL num_tasks
, index
;
439 fprintf(stderr
, "called delete_suspend_for_gc\n");
443 fprintf(stderr
, "locking msg_lock (delete) [interp=%p]\n", interp
);
445 LOCK(sched_struct
->msg_lock
);
446 /* Search the task index for GC suspend tasks */
447 num_tasks
= VTABLE_elements(interp
, sched_struct
->messages
);
448 for (index
= 0; index
< num_tasks
; ++index
) {
449 PMC
*message
= VTABLE_get_pmc_keyed_int(interp
, sched_struct
->messages
, index
);
450 if (!PMC_IS_NULL(message
)
451 && Parrot_str_equal(interp
, VTABLE_get_string(interp
, message
),
453 VTABLE_delete_keyed_int(interp
, sched_struct
->messages
, index
);
454 UNLOCK(sched_struct
->msg_lock
);
459 fprintf(stderr
, "unlocking msg_lock (delete) [interp=%p]\n", interp
);
461 UNLOCK(sched_struct
->msg_lock
);
465 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
466 "Scheduler was not initialized for this interpreter.\n");
473 =item C<void Parrot_cx_add_handler_local(PARROT_INTERP, PMC *handler)>
475 Add a handler to the current context's list of handlers.
483 Parrot_cx_add_handler_local(PARROT_INTERP
, ARGIN(PMC
*handler
))
485 ASSERT_ARGS(Parrot_cx_add_handler_local
)
486 if (PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, interp
->ctx
)))
487 Parrot_pcc_set_handlers(interp
, interp
->ctx
, Parrot_pmc_new(interp
,
488 enum_class_ResizablePMCArray
));
490 VTABLE_unshift_pmc(interp
, Parrot_pcc_get_handlers(interp
, interp
->ctx
), handler
);
496 =item C<void Parrot_cx_delete_handler_local(PARROT_INTERP, STRING
499 Remove the top task handler of a particular type from the context's list of
508 Parrot_cx_delete_handler_local(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
510 ASSERT_ARGS(Parrot_cx_delete_handler_local
)
511 PMC
*handlers
= Parrot_pcc_get_handlers(interp
, interp
->ctx
);
513 if (PMC_IS_NULL(handlers
))
514 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
515 "No handler to delete.");
517 if (STRING_IS_NULL(handler_type
) || STRING_IS_EMPTY(handler_type
))
518 VTABLE_shift_pmc(interp
, handlers
);
520 /* Loop from newest handler to oldest handler. */
521 STRING
*exception_str
= CONST_STRING(interp
, "exception");
522 STRING
*event_str
= CONST_STRING(interp
, "event");
523 STRING
*handler_str
= CONST_STRING(interp
, "ExceptionHandler");
524 const INTVAL elements
= VTABLE_elements(interp
, handlers
);
526 typedef enum { Hunknown
, Hexception
, Hevent
} Htype
;
529 Parrot_str_equal(interp
, handler_type
, exception_str
) ?
531 Parrot_str_equal(interp
, handler_type
, event_str
) ?
534 STRING
* const handler_name
= (htype
== Hexception
) ? handler_str
: (STRING
*)NULL
;
536 for (index
= 0; index
< elements
; ++index
) {
537 PMC
* const handler
= VTABLE_get_pmc_keyed_int(interp
, handlers
, index
);
538 if (!PMC_IS_NULL(handler
)) {
541 if (VTABLE_isa(interp
, handler
, handler_name
)) {
542 VTABLE_set_pmc_keyed_int(interp
, handlers
, index
, PMCNULL
);
547 if (handler
->vtable
->base_type
== enum_class_EventHandler
) {
548 VTABLE_set_pmc_keyed_int(interp
, handlers
, index
, PMCNULL
);
558 Parrot_ex_throw_from_c_args(interp
, NULL
,
559 EXCEPTION_INVALID_OPERATION
, "No handler to delete.");
566 =item C<INTVAL Parrot_cx_count_handlers_local(PARROT_INTERP, STRING
569 Count the number of active handlers of a particular type from the
570 context's list of handlers.
578 Parrot_cx_count_handlers_local(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
580 ASSERT_ARGS(Parrot_cx_count_handlers_local
)
581 PMC
* const handlers
= Parrot_pcc_get_handlers(interp
, interp
->ctx
);
584 if (PMC_IS_NULL(handlers
))
587 elements
= VTABLE_elements(interp
, handlers
);
589 if (STRING_IS_NULL(handler_type
) || STRING_IS_EMPTY(handler_type
))
592 /* Loop from newest handler to oldest handler. */
594 STRING
*exception_str
= CONST_STRING(interp
, "exception");
595 STRING
*event_str
= CONST_STRING(interp
, "event");
596 STRING
*handler_str
= CONST_STRING(interp
, "ExceptionHandler");
599 typedef enum { Hunknown
, Hexception
, Hevent
} Htype
;
602 (Parrot_str_equal(interp
, handler_type
, exception_str
)) ?
604 (Parrot_str_equal(interp
, handler_type
, event_str
)) ?
607 STRING
* const handler_name
= (htype
== Hexception
) ? handler_str
: (STRING
*)NULL
;
609 for (index
= 0; index
< elements
; ++index
) {
610 PMC
* const handler
= VTABLE_get_pmc_keyed_int(interp
, handlers
, index
);
611 if (!PMC_IS_NULL(handler
)) {
614 if (VTABLE_isa(interp
, handler
, handler_name
))
618 if (handler
->vtable
->base_type
== enum_class_EventHandler
)
633 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
635 Add a task handler to scheduler's list of handlers.
643 Parrot_cx_add_handler(PARROT_INTERP
, ARGIN(PMC
*handler
))
645 ASSERT_ARGS(Parrot_cx_add_handler
)
646 STRING
* const add_handler
= CONST_STRING(interp
, "add_handler");
647 if (!interp
->scheduler
)
648 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
649 "Scheduler was not initialized for this interpreter.\n");
651 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, add_handler
, "P->", handler
);
656 =item C<void Parrot_cx_delete_handler_typed(PARROT_INTERP, STRING
659 Remove the top task handler of a particular type from the scheduler's list of
668 Parrot_cx_delete_handler_typed(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
670 ASSERT_ARGS(Parrot_cx_delete_handler_typed
)
671 if (!interp
->scheduler
)
672 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
673 "Scheduler was not initialized for this interpreter.\n");
675 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "delete_handler"), "S->", handler_type
);
680 =item C<INTVAL Parrot_cx_count_handlers_typed(PARROT_INTERP, STRING
683 Count the number of active handlers of a particular type (event, exception) in
684 the concurrency scheduler.
692 Parrot_cx_count_handlers_typed(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
694 ASSERT_ARGS(Parrot_cx_count_handlers_typed
)
697 if (!interp
->scheduler
)
698 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
699 "Scheduler was not initialized for this interpreter.\n");
701 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "count_handlers"), "S->I", handler_type
, &count
);
710 =head2 Scheduler Message Interface Functions
712 Functions that are used to interface with the message queue in the concurrency
717 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
720 Send a message to a scheduler in a different interpreter/thread.
728 Parrot_cx_send_message(PARROT_INTERP
, ARGIN(STRING
*messagetype
), SHIM(PMC
*payload
))
730 ASSERT_ARGS(Parrot_cx_send_message
)
731 if (interp
->scheduler
) {
732 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(interp
->scheduler
);
733 PMC
*message
= Parrot_pmc_new(interp
, enum_class_SchedulerMessage
);
734 VTABLE_set_string_native(interp
, message
, messagetype
);
735 message
= VTABLE_share_ro(interp
, message
);
738 fprintf(stderr
, "sending message[interp=%p]\n", interp
);
742 fprintf(stderr
, "locking msg_lock (send) [interp=%p]\n", interp
);
744 LOCK(sched_struct
->msg_lock
);
745 VTABLE_push_pmc(interp
, sched_struct
->messages
, message
);
747 fprintf(stderr
, "unlocking msg_lock (send) [interp=%p]\n", interp
);
749 UNLOCK(sched_struct
->msg_lock
);
750 Parrot_cx_runloop_wake(interp
, interp
->scheduler
);
758 =item C<void Parrot_cx_broadcast_message(PARROT_INTERP, STRING *messagetype, PMC
761 Send a message to the schedulers in all interpreters/threads linked to this
770 Parrot_cx_broadcast_message(PARROT_INTERP
, ARGIN(STRING
*messagetype
), ARGIN_NULLOK(PMC
*data
))
772 ASSERT_ARGS(Parrot_cx_broadcast_message
)
774 LOCK(interpreter_array_mutex
);
775 for (i
= 0; i
< n_interpreters
; ++i
) {
776 Parrot_Interp other_interp
= interpreter_array
[i
];
777 if (interp
== other_interp
)
779 Parrot_cx_send_message(other_interp
, messagetype
, data
);
781 UNLOCK(interpreter_array_mutex
);
789 =head2 Task Interface Functions
791 Functions that are used to interface with a specific task in the concurrency scheduler.
795 =item C<PMC * Parrot_cx_find_handler_for_task(PARROT_INTERP, PMC *task)>
797 Retrieve a handler appropriate to a given task. If the scheduler has no
798 appropriate handler, returns PMCNULL.
805 PARROT_CAN_RETURN_NULL
807 Parrot_cx_find_handler_for_task(PARROT_INTERP
, ARGIN(PMC
*task
))
809 ASSERT_ARGS(Parrot_cx_find_handler_for_task
)
810 PMC
*handler
= PMCNULL
;
812 fprintf(stderr
, "searching for handler\n");
815 if (!interp
->scheduler
)
816 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
817 "Scheduler was not initialized for this interpreter.\n");
819 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "find_handler"), "P->P", task
, &handler
);
822 fprintf(stderr
, "done searching for handler\n");
830 =item C<PMC * Parrot_cx_find_handler_local(PARROT_INTERP, PMC *task)>
832 Retrieve a handler appropriate to a given task from the local context. If the
833 context has no appropriate handler, returns PMCNULL.
840 PARROT_CAN_RETURN_NULL
842 Parrot_cx_find_handler_local(PARROT_INTERP
, ARGIN(PMC
*task
))
844 ASSERT_ARGS(Parrot_cx_find_handler_local
)
847 * Quick&dirty way to avoid infinite recursion
848 * when an exception is thrown while looking
851 static int already_doing
= 0;
852 static PMC
* keep_context
= NULL
;
856 STRING
* const handled_str
= CONST_STRING(interp
, "handled");
857 STRING
* const iter_str
= CONST_STRING(interp
, "handler_iter");
860 Parrot_io_eprintf(interp
,
861 "** Exception caught while looking for a handler, trying next **\n");
865 * Note that we are now trying to handle the new exception,
866 * not the initial task argument (exception or whatever).
868 context
= Parrot_pcc_get_caller_ctx(interp
, keep_context
);
870 if (context
&& !PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
871 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
878 /* Exceptions store the handler iterator for rethrow, other kinds of
879 * tasks don't (though they could). */
880 if (task
->vtable
->base_type
== enum_class_Exception
881 && VTABLE_get_integer_keyed_str(interp
, task
, handled_str
) == -1) {
882 iter
= VTABLE_get_attr_str(interp
, task
, iter_str
);
883 context
= (PMC
*)VTABLE_get_pointer(interp
, task
);
886 context
= CURRENT_CONTEXT(interp
);
887 if (!PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
888 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
893 keep_context
= context
;
894 /* Loop from newest handler to oldest handler. */
895 while (!PMC_IS_NULL(iter
) && VTABLE_get_bool(interp
, iter
)) {
896 PMC
* const handler
= VTABLE_shift_pmc(interp
, iter
);
898 if (!PMC_IS_NULL(handler
)) {
899 INTVAL valid_handler
= 0;
900 if (handler
->vtable
->base_type
== enum_class_Object
)
901 Parrot_pcc_invoke_method_from_c_args(interp
, handler
, CONST_STRING(interp
, "can_handle"),
902 "P->I", task
, &valid_handler
);
904 Parrot_pcc_invoke_method_from_c_args(interp
, handler
, CONST_STRING(interp
, "can_handle"),
905 "P->I", task
, &valid_handler
);
908 if (task
->vtable
->base_type
== enum_class_Exception
) {
909 /* Store iterator and context for a later rethrow. */
910 VTABLE_set_attr_str(interp
, task
, CONST_STRING(interp
, "handler_iter"), iter
);
911 VTABLE_set_pointer(interp
, task
, context
);
920 /* Continue the search in the next context up the chain. */
921 context
= Parrot_pcc_get_caller_ctx(interp
, context
);
922 if (context
&& !PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
923 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
928 /* Reached the end of the context chain without finding a handler. */
936 =item C<void Parrot_cx_timer_invoke(PARROT_INTERP, PMC *timer)>
938 Run the associated code block for a timer event, when the timer fires.
945 Parrot_cx_timer_invoke(PARROT_INTERP
, ARGIN(PMC
*timer
))
947 ASSERT_ARGS(Parrot_cx_timer_invoke
)
948 Parrot_Timer_attributes
* const timer_struct
= PARROT_TIMER(timer
);
950 fprintf(stderr
, "current timer time: %f, %f\n",
951 timer_struct
->birthtime
+ timer_struct
->duration
,
952 Parrot_floatval_time());
954 if (!PMC_IS_NULL(timer_struct
->codeblock
)) {
955 Parrot_pcc_invoke_sub_from_c_args(interp
,
956 timer_struct
->codeblock
, "->");
962 =item C<void Parrot_cx_invoke_callback(PARROT_INTERP, PMC *callback)>
964 Run the associated code block for a callback event.
971 Parrot_cx_invoke_callback(PARROT_INTERP
, ARGIN(PMC
*callback
))
973 ASSERT_ARGS(Parrot_cx_invoke_callback
)
974 Parrot_Task_attributes
* const task_struct
= PARROT_TASK(callback
);
975 if (!PMC_IS_NULL(task_struct
->data
)) {
976 Parrot_run_callback(interp
, task_struct
->data
,
977 task_struct
->cb_data
);
985 =head2 Opcode Functions
987 Functions that are called from within opcodes, that take and return an
988 opcode_t* to allow for changing the code flow.
993 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
996 Add a sleep timer to the scheduler. This function is called by the C<sleep>
1004 PARROT_WARN_UNUSED_RESULT
1005 PARROT_CAN_RETURN_NULL
1007 Parrot_cx_schedule_sleep(PARROT_INTERP
, FLOATVAL time
, ARGIN_NULLOK(opcode_t
*next
))
1009 ASSERT_ARGS(Parrot_cx_schedule_sleep
)
1010 #ifdef PARROT_HAS_THREADS
1011 Parrot_cond condition
;
1013 const FLOATVAL timer_end
= time
+ Parrot_floatval_time();
1014 struct timespec time_struct
;
1016 /* Tell the scheduler runloop to wake, this is a good time to process
1018 Parrot_cx_runloop_wake(interp
, interp
->scheduler
);
1020 /* Tell this thread to sleep for the requested time. */
1021 COND_INIT(condition
);
1024 time_struct
.tv_sec
= (time_t) timer_end
;
1025 time_struct
.tv_nsec
= (long)((timer_end
- time_struct
.tv_sec
)*1000.0f
) *1000L*1000L;
1026 COND_TIMED_WAIT(condition
, lock
, &time_struct
);
1028 COND_DESTROY(condition
);
1029 MUTEX_DESTROY(lock
);
1031 /* A more primitive, platform-specific, non-threaded form of sleep. */
1033 /* prevent integer overflow when converting to microseconds */
1034 const int seconds
= floor(time
);
1035 Parrot_sleep(seconds
);
1038 Parrot_usleep((UINTVAL
) time
*1000000);
1048 =head2 Internal Functions
1050 Functions that are only used within the scheduler.
1054 =item C<static void scheduler_process_wait_list(PARROT_INTERP, PMC *scheduler)>
1056 Scheduler maintenance, scan the list of waiting tasks to see if any are ready
1057 to become active tasks.
1064 scheduler_process_wait_list(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
1066 ASSERT_ARGS(scheduler_process_wait_list
)
1067 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(scheduler
);
1068 INTVAL num_tasks
, index
;
1070 /* Sweep the wait list for completed timers */
1071 num_tasks
= VTABLE_elements(interp
, sched_struct
->wait_index
);
1072 for (index
= 0; index
< num_tasks
; ++index
) {
1073 INTVAL tid
= VTABLE_get_integer_keyed_int(interp
, sched_struct
->wait_index
, index
);
1075 PMC
*task
= VTABLE_get_pmc_keyed_int(interp
, sched_struct
->task_list
, tid
);
1076 if (PMC_IS_NULL(task
)) {
1077 /* Cleanup expired tasks. */
1078 VTABLE_set_integer_keyed_int(interp
, sched_struct
->wait_index
, index
, 0);
1081 /* Move the timer to the active task list if the timer has
1083 FLOATVAL timer_end_time
= VTABLE_get_number_keyed_int(interp
,
1084 task
, PARROT_TIMER_NSEC
);
1085 if (timer_end_time
<= Parrot_floatval_time()) {
1086 VTABLE_push_integer(interp
, sched_struct
->task_index
, tid
);
1087 VTABLE_set_integer_keyed_int(interp
, sched_struct
->wait_index
, index
, 0);
1088 Parrot_cx_schedule_repeat(interp
, task
);
1089 SCHEDULER_cache_valid_CLEAR(scheduler
);
1100 =item C<static void scheduler_process_messages(PARROT_INTERP, PMC *scheduler)>
1102 Scheduler maintenance, scan the list of messages sent from other schedulers and
1103 take appropriate action on any received.
1110 scheduler_process_messages(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
1112 ASSERT_ARGS(scheduler_process_messages
)
1113 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(scheduler
);
1116 STRING
*suspend_str
= CONST_STRING(interp
, "suspend_for_gc");
1119 fprintf(stderr
, "processing messages [interp=%p]\n", interp
);
1122 while (VTABLE_elements(interp
, sched_struct
->messages
) > 0) {
1124 fprintf(stderr
, "locking msg_lock (process) [interp=%p]\n", interp
);
1126 LOCK(sched_struct
->msg_lock
);
1127 message
= VTABLE_pop_pmc(interp
, sched_struct
->messages
);
1129 fprintf(stderr
, "unlocking msg_lock (process) [interp=%p]\n", interp
);
1131 UNLOCK(sched_struct
->msg_lock
);
1132 if (!PMC_IS_NULL(message
)
1133 && Parrot_str_equal(interp
, VTABLE_get_string(interp
, message
),
1136 fprintf(stderr
, "found a suspend, suspending [interp=%p]\n", interp
);
1138 pt_suspend_self_for_gc(interp
);
1155 * c-file-style: "parrot"
1157 * vim: expandtab shiftwidth=4: