2 Copyright (C) 2007-2009, 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"
22 #include "pmc/pmc_scheduler.h"
23 #include "pmc/pmc_task.h"
24 #include "pmc/pmc_timer.h"
25 #include "pmc/pmc_context.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
= 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
= 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
*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
*callback
= 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
, pmc_new(interp
, enum_class_ResizablePMCArray
));
489 VTABLE_unshift_pmc(interp
, Parrot_pcc_get_handlers(interp
, interp
->ctx
), handler
);
495 =item C<void Parrot_cx_delete_handler_local(PARROT_INTERP, STRING
498 Remove the top task handler of a particular type from the context's list of
507 Parrot_cx_delete_handler_local(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
509 ASSERT_ARGS(Parrot_cx_delete_handler_local
)
510 PMC
*handlers
= Parrot_pcc_get_handlers(interp
, interp
->ctx
);
512 if (PMC_IS_NULL(handlers
))
513 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
514 "No handler to delete.");
516 if (STRING_IS_NULL(handler_type
) || STRING_IS_EMPTY(handler_type
))
517 VTABLE_shift_pmc(interp
, handlers
);
519 /* Loop from newest handler to oldest handler. */
520 STRING
*exception_str
= CONST_STRING(interp
, "exception");
521 STRING
*event_str
= CONST_STRING(interp
, "event");
522 STRING
*handler_str
= CONST_STRING(interp
, "ExceptionHandler");
523 const INTVAL elements
= VTABLE_elements(interp
, handlers
);
525 typedef enum { Hunknown
, Hexception
, Hevent
} Htype
;
528 Parrot_str_equal(interp
, handler_type
, exception_str
) ?
530 Parrot_str_equal(interp
, handler_type
, event_str
) ?
533 STRING
* const handler_name
= (htype
== Hexception
) ?
534 handler_str
: (STRING
*) NULL
;
536 for (index
= 0; index
< elements
; ++index
) {
537 PMC
*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
*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
) ?
608 handler_str
: (STRING
*) NULL
;
610 for (index
= 0; index
< elements
; ++index
) {
611 PMC
*handler
= VTABLE_get_pmc_keyed_int(interp
, handlers
, index
);
612 if (!PMC_IS_NULL(handler
)) {
615 if (VTABLE_isa(interp
, handler
, handler_name
))
619 if (handler
->vtable
->base_type
== enum_class_EventHandler
)
634 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
636 Add a task handler to scheduler's list of handlers.
644 Parrot_cx_add_handler(PARROT_INTERP
, ARGIN(PMC
*handler
))
646 ASSERT_ARGS(Parrot_cx_add_handler
)
647 STRING
*add_handler
= CONST_STRING(interp
, "add_handler");
648 if (!interp
->scheduler
)
649 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
650 "Scheduler was not initialized for this interpreter.\n");
652 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, add_handler
, "P->", handler
);
657 =item C<void Parrot_cx_delete_handler_typed(PARROT_INTERP, STRING
660 Remove the top task handler of a particular type from the scheduler's list of
669 Parrot_cx_delete_handler_typed(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
671 ASSERT_ARGS(Parrot_cx_delete_handler_typed
)
672 if (!interp
->scheduler
)
673 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
674 "Scheduler was not initialized for this interpreter.\n");
676 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "delete_handler"), "S->", handler_type
);
681 =item C<INTVAL Parrot_cx_count_handlers_typed(PARROT_INTERP, STRING
684 Count the number of active handlers of a particular type (event, exception) in
685 the concurrency scheduler.
693 Parrot_cx_count_handlers_typed(PARROT_INTERP
, ARGIN(STRING
*handler_type
))
695 ASSERT_ARGS(Parrot_cx_count_handlers_typed
)
698 if (!interp
->scheduler
)
699 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
700 "Scheduler was not initialized for this interpreter.\n");
702 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "count_handlers"), "S->I", handler_type
, &count
);
711 =head2 Scheduler Message Interface Functions
713 Functions that are used to interface with the message queue in the concurrency
718 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
721 Send a message to a scheduler in a different interpreter/thread.
729 Parrot_cx_send_message(PARROT_INTERP
, ARGIN(STRING
*messagetype
), SHIM(PMC
*payload
))
731 ASSERT_ARGS(Parrot_cx_send_message
)
732 if (interp
->scheduler
) {
733 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(interp
->scheduler
);
734 PMC
*message
= pmc_new(interp
, enum_class_SchedulerMessage
);
735 VTABLE_set_string_native(interp
, message
, messagetype
);
736 message
= VTABLE_share_ro(interp
, message
);
739 fprintf(stderr
, "sending message[interp=%p]\n", interp
);
743 fprintf(stderr
, "locking msg_lock (send) [interp=%p]\n", interp
);
745 LOCK(sched_struct
->msg_lock
);
746 VTABLE_push_pmc(interp
, sched_struct
->messages
, message
);
748 fprintf(stderr
, "unlocking msg_lock (send) [interp=%p]\n", interp
);
750 UNLOCK(sched_struct
->msg_lock
);
751 Parrot_cx_runloop_wake(interp
, interp
->scheduler
);
759 =item C<void Parrot_cx_broadcast_message(PARROT_INTERP, STRING *messagetype, PMC
762 Send a message to the schedulers in all interpreters/threads linked to this
771 Parrot_cx_broadcast_message(PARROT_INTERP
, ARGIN(STRING
*messagetype
), ARGIN_NULLOK(PMC
*data
))
773 ASSERT_ARGS(Parrot_cx_broadcast_message
)
775 LOCK(interpreter_array_mutex
);
776 for (i
= 0; i
< n_interpreters
; ++i
) {
777 Parrot_Interp other_interp
= interpreter_array
[i
];
778 if (interp
== other_interp
)
780 Parrot_cx_send_message(other_interp
, messagetype
, data
);
782 UNLOCK(interpreter_array_mutex
);
790 =head2 Task Interface Functions
792 Functions that are used to interface with a specific task in the concurrency scheduler.
796 =item C<PMC * Parrot_cx_find_handler_for_task(PARROT_INTERP, PMC *task)>
798 Retrieve a handler appropriate to a given task. If the scheduler has no
799 appropriate handler, returns PMCNULL.
806 PARROT_CAN_RETURN_NULL
808 Parrot_cx_find_handler_for_task(PARROT_INTERP
, ARGIN(PMC
*task
))
810 ASSERT_ARGS(Parrot_cx_find_handler_for_task
)
811 PMC
*handler
= PMCNULL
;
813 fprintf(stderr
, "searching for handler\n");
816 if (!interp
->scheduler
)
817 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_INVALID_OPERATION
,
818 "Scheduler was not initialized for this interpreter.\n");
820 Parrot_pcc_invoke_method_from_c_args(interp
, interp
->scheduler
, CONST_STRING(interp
, "find_handler"), "P->P", task
, &handler
);
823 fprintf(stderr
, "done searching for handler\n");
831 =item C<PMC * Parrot_cx_find_handler_local(PARROT_INTERP, PMC *task)>
833 Retrieve a handler appropriate to a given task from the local context. If the
834 context has no appropriate handler, returns PMCNULL.
841 PARROT_CAN_RETURN_NULL
843 Parrot_cx_find_handler_local(PARROT_INTERP
, ARGIN(PMC
*task
))
845 ASSERT_ARGS(Parrot_cx_find_handler_local
)
848 * Quick&dirty way to avoid infinite recursion
849 * when an exception is thrown while looking
852 static int already_doing
= 0;
853 static PMC
* keep_context
= NULL
;
857 STRING
* const handled_str
= CONST_STRING(interp
, "handled");
858 STRING
* const iter_str
= CONST_STRING(interp
, "handler_iter");
861 Parrot_io_eprintf(interp
,
862 "** Exception caught while looking for a handler, trying next **\n");
866 * Note that we are now trying to handle the new exception,
867 * not the initial task argument (exception or whatever).
869 context
= Parrot_pcc_get_caller_ctx(interp
, keep_context
);
871 if (context
&& !PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
872 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
879 /* Exceptions store the handler iterator for rethrow, other kinds of
880 * tasks don't (though they could). */
881 if (task
->vtable
->base_type
== enum_class_Exception
882 && VTABLE_get_integer_keyed_str(interp
, task
, handled_str
) == -1) {
883 iter
= VTABLE_get_attr_str(interp
, task
, iter_str
);
884 context
= (PMC
*)VTABLE_get_pointer(interp
, task
);
887 context
= CURRENT_CONTEXT(interp
);
888 if (!PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
889 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
894 keep_context
= context
;
895 /* Loop from newest handler to oldest handler. */
896 while (!PMC_IS_NULL(iter
) && VTABLE_get_bool(interp
, iter
)) {
897 PMC
*handler
= VTABLE_shift_pmc(interp
, iter
);
899 if (!PMC_IS_NULL(handler
)) {
900 INTVAL valid_handler
= 0;
901 if (handler
->vtable
->base_type
== enum_class_Object
)
902 Parrot_pcc_invoke_method_from_c_args(interp
, handler
, CONST_STRING(interp
, "can_handle"),
903 "P->I", task
, &valid_handler
);
905 Parrot_pcc_invoke_method_from_c_args(interp
, handler
, CONST_STRING(interp
, "can_handle"),
906 "P->I", task
, &valid_handler
);
909 if (task
->vtable
->base_type
== enum_class_Exception
) {
910 /* Store iterator and context for a later rethrow. */
911 VTABLE_set_attr_str(interp
, task
, CONST_STRING(interp
, "handler_iter"), iter
);
912 VTABLE_set_pointer(interp
, task
, context
);
921 /* Continue the search in the next context up the chain. */
922 context
= Parrot_pcc_get_caller_ctx(interp
, context
);
923 if (context
&& !PMC_IS_NULL(Parrot_pcc_get_handlers(interp
, context
)))
924 iter
= VTABLE_get_iter(interp
, Parrot_pcc_get_handlers(interp
, context
));
929 /* Reached the end of the context chain without finding a handler. */
937 =item C<void Parrot_cx_timer_invoke(PARROT_INTERP, PMC *timer)>
939 Run the associated code block for a timer event, when the timer fires.
946 Parrot_cx_timer_invoke(PARROT_INTERP
, ARGIN(PMC
*timer
))
948 ASSERT_ARGS(Parrot_cx_timer_invoke
)
949 Parrot_Timer_attributes
* const timer_struct
= PARROT_TIMER(timer
);
951 fprintf(stderr
, "current timer time: %f, %f\n",
952 timer_struct
->birthtime
+ timer_struct
->duration
,
953 Parrot_floatval_time());
955 if (!PMC_IS_NULL(timer_struct
->codeblock
)) {
956 Parrot_pcc_invoke_sub_from_c_args(interp
,
957 timer_struct
->codeblock
, "->");
963 =item C<void Parrot_cx_invoke_callback(PARROT_INTERP, PMC *callback)>
965 Run the associated code block for a callback event.
972 Parrot_cx_invoke_callback(PARROT_INTERP
, ARGIN(PMC
*callback
))
974 ASSERT_ARGS(Parrot_cx_invoke_callback
)
975 Parrot_Task_attributes
* const task_struct
= PARROT_TASK(callback
);
976 if (!PMC_IS_NULL(task_struct
->data
)) {
977 Parrot_run_callback(interp
, task_struct
->data
,
978 task_struct
->cb_data
);
986 =head2 Opcode Functions
988 Functions that are called from within opcodes, that take and return an
989 opcode_t* to allow for changing the code flow.
994 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
997 Add a sleep timer to the scheduler. This function is called by the C<sleep>
1005 PARROT_WARN_UNUSED_RESULT
1006 PARROT_CAN_RETURN_NULL
1008 Parrot_cx_schedule_sleep(PARROT_INTERP
, FLOATVAL time
, ARGIN_NULLOK(opcode_t
*next
))
1010 ASSERT_ARGS(Parrot_cx_schedule_sleep
)
1011 #ifdef PARROT_HAS_THREADS
1012 Parrot_cond condition
;
1014 const FLOATVAL timer_end
= time
+ Parrot_floatval_time();
1015 struct timespec time_struct
;
1017 /* Tell the scheduler runloop to wake, this is a good time to process
1019 Parrot_cx_runloop_wake(interp
, interp
->scheduler
);
1021 /* Tell this thread to sleep for the requested time. */
1022 COND_INIT(condition
);
1025 time_struct
.tv_sec
= (time_t) timer_end
;
1026 time_struct
.tv_nsec
= (long)((timer_end
- time_struct
.tv_sec
)*1000.0f
) *1000L*1000L;
1027 COND_TIMED_WAIT(condition
, lock
, &time_struct
);
1029 COND_DESTROY(condition
);
1030 MUTEX_DESTROY(lock
);
1032 /* A more primitive, platform-specific, non-threaded form of sleep. */
1034 /* prevent integer overflow when converting to microseconds */
1035 const int seconds
= floor(time
);
1036 Parrot_sleep(seconds
);
1039 Parrot_usleep((UINTVAL
) time
*1000000);
1049 =head2 Internal Functions
1051 Functions that are only used within the scheduler.
1055 =item C<static void scheduler_process_wait_list(PARROT_INTERP, PMC *scheduler)>
1057 Scheduler maintenance, scan the list of waiting tasks to see if any are ready
1058 to become active tasks.
1065 scheduler_process_wait_list(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
1067 ASSERT_ARGS(scheduler_process_wait_list
)
1068 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(scheduler
);
1069 INTVAL num_tasks
, index
;
1071 /* Sweep the wait list for completed timers */
1072 num_tasks
= VTABLE_elements(interp
, sched_struct
->wait_index
);
1073 for (index
= 0; index
< num_tasks
; index
++) {
1074 INTVAL tid
= VTABLE_get_integer_keyed_int(interp
, sched_struct
->wait_index
, index
);
1076 PMC
*task
= VTABLE_get_pmc_keyed_int(interp
, sched_struct
->task_list
, tid
);
1077 if (PMC_IS_NULL(task
)) {
1078 /* Cleanup expired tasks. */
1079 VTABLE_set_integer_keyed_int(interp
, sched_struct
->wait_index
, index
, 0);
1082 /* Move the timer to the active task list if the timer has
1084 FLOATVAL timer_end_time
= VTABLE_get_number_keyed_int(interp
,
1085 task
, PARROT_TIMER_NSEC
);
1086 if (timer_end_time
<= Parrot_floatval_time()) {
1087 VTABLE_push_integer(interp
, sched_struct
->task_index
, tid
);
1088 VTABLE_set_integer_keyed_int(interp
, sched_struct
->wait_index
, index
, 0);
1089 Parrot_cx_schedule_repeat(interp
, task
);
1090 SCHEDULER_cache_valid_CLEAR(scheduler
);
1101 =item C<static void scheduler_process_messages(PARROT_INTERP, PMC *scheduler)>
1103 Scheduler maintenance, scan the list of messages sent from other schedulers and
1104 take appropriate action on any received.
1111 scheduler_process_messages(PARROT_INTERP
, ARGMOD(PMC
*scheduler
))
1113 ASSERT_ARGS(scheduler_process_messages
)
1114 Parrot_Scheduler_attributes
* sched_struct
= PARROT_SCHEDULER(scheduler
);
1117 STRING
*suspend_str
= CONST_STRING(interp
, "suspend_for_gc");
1120 fprintf(stderr
, "processing messages [interp=%p]\n", interp
);
1123 while (VTABLE_elements(interp
, sched_struct
->messages
) > 0) {
1125 fprintf(stderr
, "locking msg_lock (process) [interp=%p]\n", interp
);
1127 LOCK(sched_struct
->msg_lock
);
1128 message
= VTABLE_pop_pmc(interp
, sched_struct
->messages
);
1130 fprintf(stderr
, "unlocking msg_lock (process) [interp=%p]\n", interp
);
1132 UNLOCK(sched_struct
->msg_lock
);
1133 if (!PMC_IS_NULL(message
)
1134 && Parrot_str_equal(interp
, VTABLE_get_string(interp
, message
),
1137 fprintf(stderr
, "found a suspend, suspending [interp=%p]\n", interp
);
1139 pt_suspend_self_for_gc(interp
);
1156 * c-file-style: "parrot"
1158 * vim: expandtab shiftwidth=4: