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/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"
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.
66 =item C<void Parrot_cx_init_scheduler(PARROT_INTERP)>
68 Initalize the concurrency scheduler for the interpreter.
75 Parrot_cx_init_scheduler(PARROT_INTERP
)
77 ASSERT_ARGS(Parrot_cx_init_scheduler
)
78 if (!interp
->parent_interpreter
) {
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.
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.
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
))
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
);
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
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
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
);
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).
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.
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.
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
270 PARROT_CAN_RETURN_NULL
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>.
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.
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
);
339 PMC
* const repeat_task
= VTABLE_clone(interp
, task
);
340 VTABLE_set_number_keyed_int(interp
, repeat_task
, PARROT_TIMER_NSEC
, duration
);
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
355 Create a new callback event, with an argument for the call.
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.
389 Parrot_cx_request_suspend_for_gc(PARROT_INTERP
)
391 ASSERT_ARGS(Parrot_cx_request_suspend_for_gc
)
393 fprintf(stderr
, "requesting gc suspend [interp=%p]\n", interp
);
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.
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.)
435 PARROT_CAN_RETURN_NULL
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
;
446 fprintf(stderr
, "called delete_suspend_for_gc\n");
450 fprintf(stderr
, "locking msg_lock (delete) [interp=%p]\n", interp
);
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
),
460 VTABLE_delete_keyed_int(interp
, sched_struct
->messages
, index
);
461 UNLOCK(sched_struct
->msg_lock
);
466 fprintf(stderr
, "unlocking msg_lock (delete) [interp=%p]\n", interp
);
468 UNLOCK(sched_struct
->msg_lock
);
472 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
473 "Scheduler was not initialized for this interpreter.\n");
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.
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
506 Remove the top task handler of a particular type from the context's list of
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
);
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
);
533 typedef enum { Hunknown
, Hexception
, Hevent
} Htype
;
536 Parrot_str_equal(interp
, handler_type
, exception_str
) ?
538 Parrot_str_equal(interp
, handler_type
, event_str
) ?
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
)) {
548 if (VTABLE_isa(interp
, handler
, handler_name
)) {
549 VTABLE_set_pmc_keyed_int(interp
, handlers
, index
, PMCNULL
);
554 if (handler
->vtable
->base_type
== enum_class_EventHandler
) {
555 VTABLE_set_pmc_keyed_int(interp
, handlers
, index
, PMCNULL
);
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
576 Count the number of active handlers of a particular type from the
577 context's list of handlers.
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
);
591 if (PMC_IS_NULL(handlers
))
594 elements
= VTABLE_elements(interp
, handlers
);
596 if (STRING_IS_NULL(handler_type
) || STRING_IS_EMPTY(handler_type
))
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");
606 typedef enum { Hunknown
, Hexception
, Hevent
} Htype
;
609 (Parrot_str_equal(interp
, handler_type
, exception_str
)) ?
611 (Parrot_str_equal(interp
, handler_type
, event_str
)) ?
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
)) {
621 if (VTABLE_isa(interp
, handler
, handler_name
))
625 if (handler
->vtable
->base_type
== enum_class_EventHandler
)
640 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
642 Add a task handler to scheduler's list of handlers.
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
666 Remove the top task handler of a particular type from the scheduler's list of
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
690 Count the number of active handlers of a particular type (event, exception) in
691 the concurrency scheduler.
699 Parrot_cx_count_handlers_typed(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
701 ASSERT_ARGS(Parrot_cx_count_handlers_typed
)
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
);
717 =head2 Scheduler Message Interface Functions
719 Functions that are used to interface with the message queue in the concurrency
724 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
727 Send a message to a scheduler in a different interpreter/thread.
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
);
745 fprintf(stderr
, "sending message[interp=%p]\n", interp
);
749 fprintf(stderr
, "locking msg_lock (send) [interp=%p]\n", interp
);
751 LOCK(sched_struct
->msg_lock
);
752 VTABLE_push_pmc(interp
, sched_struct
->messages
, message
);
754 fprintf(stderr
, "unlocking msg_lock (send) [interp=%p]\n", interp
);
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
768 Send a message to the schedulers in all interpreters/threads linked to this
777 Parrot_cx_broadcast_message(PARROT_INTERP
, ARGIN(STRING
*messagetype
), ARGIN_NULLOK(PMC
*data
))
779 ASSERT_ARGS(Parrot_cx_broadcast_message
)
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
)
786 Parrot_cx_send_message(other_interp
, messagetype
, data
);
788 UNLOCK(interpreter_array_mutex
);
796 =head2 Task Interface Functions
798 Functions that are used to interface with a specific task in the concurrency scheduler.
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.
812 PARROT_CAN_RETURN_NULL
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
;
819 fprintf(stderr
, "searching for handler\n");
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
);
829 fprintf(stderr
, "done searching for handler\n");
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.
847 PARROT_CAN_RETURN_NULL
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
858 static int already_doing
= 0;
859 static PMC
* keep_context
= NULL
;
863 STRING
* const handled_str
= CONST_STRING(interp
, "handled");
864 STRING
* const iter_str
= CONST_STRING(interp
, "handler_iter");
867 Parrot_io_eprintf(interp
,
868 "** Exception caught while looking for a handler, trying next **\n");
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
);
877 if (context
&& !PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
878 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
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
);
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
));
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
);
911 Parrot_pcc_invoke_method_from_c_args(interp
, handler
, CONST_STRING(interp
, "can_handle"),
912 "P->I", task
, &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
);
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
));
935 /* Reached the end of the context chain without finding a handler. */
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.
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
);
957 fprintf(stderr
, "current timer time: %f, %f\n",
958 timer_struct
->birthtime
+ timer_struct
->duration
,
959 Parrot_floatval_time());
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.
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
);
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.
999 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
1002 Add a sleep timer to the scheduler. This function is called by the C<sleep>
1010 PARROT_WARN_UNUSED_RESULT
1011 PARROT_CAN_RETURN_NULL
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
;
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
1024 Parrot_cx_runloop_wake(interp
, interp
->scheduler
);
1026 /* Tell this thread to sleep for the requested time. */
1027 COND_INIT(condition
);
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
);
1034 COND_DESTROY(condition
);
1035 MUTEX_DESTROY(lock
);
1037 /* A more primitive, platform-specific, non-threaded form of sleep. */
1039 /* prevent integer overflow when converting to microseconds */
1040 const int seconds
= floor(time
);
1041 Parrot_sleep(seconds
);
1044 Parrot_usleep((UINTVAL
) time
*1000000);
1054 =head2 Internal Functions
1056 Functions that are only used within the scheduler.
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.
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
);
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);
1087 /* Move the timer to the active task list if the timer has
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
);
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.
1116 scheduler_process_messages(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
1118 ASSERT_ARGS(scheduler_process_messages
)
1119 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(scheduler
);
1122 STRING
*suspend_str
= CONST_STRING(interp
, "suspend_for_gc");
1125 fprintf(stderr
, "processing messages [interp=%p]\n", interp
);
1128 while (VTABLE_elements(interp
, sched_struct
->messages
) > 0) {
1130 fprintf(stderr
, "locking msg_lock (process) [interp=%p]\n", interp
);
1132 LOCK(sched_struct
->msg_lock
);
1133 message
= VTABLE_pop_pmc(interp
, sched_struct
->messages
);
1135 fprintf(stderr
, "unlocking msg_lock (process) [interp=%p]\n", interp
);
1137 UNLOCK(sched_struct
->msg_lock
);
1138 if (!PMC_IS_NULL(message
)
1139 && Parrot_str_equal(interp
, VTABLE_get_string(interp
, message
),
1142 fprintf(stderr
, "found a suspend, suspending [interp=%p]\n", interp
);
1144 pt_suspend_self_for_gc(interp
);
1161 * c-file-style: "parrot"
1163 * vim: expandtab shiftwidth=4: