revert - didn't do what I wanted
[parrot.git] / src / scheduler.c
bloba3094588e1d3d9a6cf6a38510cb9719d23822b63
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/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"
29 #define CX_DEBUG 0
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.
63 =over 4
65 =item C<void Parrot_cx_init_scheduler(PARROT_INTERP)>
67 Initalize the concurrency scheduler for the interpreter.
69 =cut
73 void
74 Parrot_cx_init_scheduler(PARROT_INTERP)
76 ASSERT_ARGS(Parrot_cx_init_scheduler)
77 if (!interp->parent_interpreter) {
78 PMC *scheduler;
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.
96 =cut
100 void
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.
116 =cut
120 PARROT_EXPORT
121 void
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);
148 else {
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
157 * tasks. */
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
170 messages, etc.
172 =cut
176 void
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);
186 return;
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).
196 =cut
200 void
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.
215 =cut
219 PARROT_EXPORT
220 void
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.
235 =cut
239 PARROT_EXPORT
240 void
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
256 from the list.
258 =cut
262 PARROT_EXPORT
263 PARROT_CAN_RETURN_NULL
264 PMC *
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>.
283 =cut
287 PARROT_EXPORT
288 void
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.
318 =cut
322 PARROT_EXPORT
323 void
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);
331 if (repeat != 0) {
332 PMC * const repeat_task = VTABLE_clone(interp, task);
333 VTABLE_set_number_keyed_int(interp, repeat_task, PARROT_TIMER_NSEC, duration);
335 if (repeat > 0)
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
346 *ext_data)>
348 Create a new callback event, with an argument for the call.
350 =cut
354 PARROT_EXPORT
355 void
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.
376 =cut
380 PARROT_EXPORT
381 void
382 Parrot_cx_request_suspend_for_gc(PARROT_INTERP)
384 ASSERT_ARGS(Parrot_cx_request_suspend_for_gc)
385 #if CX_DEBUG
386 fprintf(stderr, "requesting gc suspend [interp=%p]\n", interp);
387 #endif
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.
397 =cut
401 PARROT_EXPORT
402 void
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.)
423 =cut
427 PARROT_EXPORT
428 PARROT_CAN_RETURN_NULL
429 PMC *
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;
438 #if CX_DEBUG
439 fprintf(stderr, "called delete_suspend_for_gc\n");
440 #endif
442 #if CX_DEBUG
443 fprintf(stderr, "locking msg_lock (delete) [interp=%p]\n", interp);
444 #endif
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),
452 suspend_str)) {
453 VTABLE_delete_keyed_int(interp, sched_struct->messages, index);
454 UNLOCK(sched_struct->msg_lock);
455 return message;
458 #if CX_DEBUG
459 fprintf(stderr, "unlocking msg_lock (delete) [interp=%p]\n", interp);
460 #endif
461 UNLOCK(sched_struct->msg_lock);
464 else
465 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
466 "Scheduler was not initialized for this interpreter.\n");
468 return PMCNULL;
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.
477 =cut
481 PARROT_EXPORT
482 void
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
497 *handler_type)>
499 Remove the top task handler of a particular type from the context's list of
500 handlers.
502 =cut
506 PARROT_EXPORT
507 void
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);
519 else {
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);
525 INTVAL index;
526 typedef enum { Hunknown, Hexception, Hevent } Htype;
528 const Htype htype =
529 Parrot_str_equal(interp, handler_type, exception_str) ?
530 Hexception :
531 Parrot_str_equal(interp, handler_type, event_str) ?
532 Hevent :
533 Hunknown;
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)) {
539 switch (htype) {
540 case Hexception:
541 if (VTABLE_isa(interp, handler, handler_name)) {
542 VTABLE_set_pmc_keyed_int(interp, handlers, index, PMCNULL);
543 return;
545 break;
546 case Hevent:
547 if (handler->vtable->base_type == enum_class_EventHandler) {
548 VTABLE_set_pmc_keyed_int(interp, handlers, index, PMCNULL);
549 return;
551 break;
552 default:
553 break;
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
567 *handler_type)>
569 Count the number of active handlers of a particular type from the
570 context's list of handlers.
572 =cut
576 PARROT_EXPORT
577 INTVAL
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);
582 INTVAL elements;
584 if (PMC_IS_NULL(handlers))
585 return 0;
587 elements = VTABLE_elements(interp, handlers);
589 if (STRING_IS_NULL(handler_type) || STRING_IS_EMPTY(handler_type))
590 return elements;
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");
597 INTVAL count = 0;
598 INTVAL index;
599 typedef enum { Hunknown, Hexception, Hevent } Htype;
601 const Htype htype =
602 (Parrot_str_equal(interp, handler_type, exception_str)) ?
603 Hexception :
604 (Parrot_str_equal(interp, handler_type, event_str)) ?
605 Hevent :
606 Hunknown;
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)) {
612 switch (htype) {
613 case Hexception:
614 if (VTABLE_isa(interp, handler, handler_name))
615 ++count;
616 break;
617 case Hevent:
618 if (handler->vtable->base_type == enum_class_EventHandler)
619 ++count;
620 break;
621 default:
622 break;
626 return count;
633 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
635 Add a task handler to scheduler's list of handlers.
637 =cut
641 PARROT_EXPORT
642 void
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
657 *handler_type)>
659 Remove the top task handler of a particular type from the scheduler's list of
660 handlers.
662 =cut
666 PARROT_EXPORT
667 void
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
681 *handler_type)>
683 Count the number of active handlers of a particular type (event, exception) in
684 the concurrency scheduler.
686 =cut
690 PARROT_EXPORT
691 INTVAL
692 Parrot_cx_count_handlers_typed(PARROT_INTERP, ARGIN(STRING *handler_type))
694 ASSERT_ARGS(Parrot_cx_count_handlers_typed)
695 INTVAL count = 0;
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);
703 return count;
708 =back
710 =head2 Scheduler Message Interface Functions
712 Functions that are used to interface with the message queue in the concurrency
713 scheduler.
715 =over 4
717 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
718 *payload)>
720 Send a message to a scheduler in a different interpreter/thread.
722 =cut
726 PARROT_EXPORT
727 void
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);
737 #if CX_DEBUG
738 fprintf(stderr, "sending message[interp=%p]\n", interp);
739 #endif
741 #if CX_DEBUG
742 fprintf(stderr, "locking msg_lock (send) [interp=%p]\n", interp);
743 #endif
744 LOCK(sched_struct->msg_lock);
745 VTABLE_push_pmc(interp, sched_struct->messages, message);
746 #if CX_DEBUG
747 fprintf(stderr, "unlocking msg_lock (send) [interp=%p]\n", interp);
748 #endif
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
759 *data)>
761 Send a message to the schedulers in all interpreters/threads linked to this
762 one.
764 =cut
768 PARROT_EXPORT
769 void
770 Parrot_cx_broadcast_message(PARROT_INTERP, ARGIN(STRING *messagetype), ARGIN_NULLOK(PMC *data))
772 ASSERT_ARGS(Parrot_cx_broadcast_message)
773 UINTVAL i;
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)
778 continue;
779 Parrot_cx_send_message(other_interp, messagetype, data);
781 UNLOCK(interpreter_array_mutex);
787 =back
789 =head2 Task Interface Functions
791 Functions that are used to interface with a specific task in the concurrency scheduler.
793 =over 4
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.
800 =cut
804 PARROT_EXPORT
805 PARROT_CAN_RETURN_NULL
806 PMC *
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;
811 #if CX_DEBUG
812 fprintf(stderr, "searching for handler\n");
813 #endif
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);
821 #if CX_DEBUG
822 fprintf(stderr, "done searching for handler\n");
823 #endif
825 return handler;
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.
835 =cut
839 PARROT_EXPORT
840 PARROT_CAN_RETURN_NULL
841 PMC *
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
849 * for a handler
851 static int already_doing = 0;
852 static PMC * keep_context = NULL;
854 PMC *context;
855 PMC *iter = PMCNULL;
856 STRING * const handled_str = CONST_STRING(interp, "handled");
857 STRING * const iter_str = CONST_STRING(interp, "handler_iter");
859 if (already_doing) {
860 Parrot_io_eprintf(interp,
861 "** Exception caught while looking for a handler, trying next **\n");
862 if (! keep_context)
863 return NULL;
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);
869 keep_context = NULL;
870 if (context && !PMC_IS_NULL(Parrot_pcc_get_handlers(interp, context)))
871 iter = VTABLE_get_iter(interp, Parrot_pcc_get_handlers(interp, context));
872 else
873 iter = PMCNULL;
875 else {
876 ++already_doing;
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);
885 else {
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));
892 while (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);
903 else
904 Parrot_pcc_invoke_method_from_c_args(interp, handler, CONST_STRING(interp, "can_handle"),
905 "P->I", task, &valid_handler);
907 if (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);
913 --already_doing;
914 keep_context = NULL;
915 return handler;
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));
924 else
925 iter = PMCNULL;
928 /* Reached the end of the context chain without finding a handler. */
930 --already_doing;
931 return PMCNULL;
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.
940 =cut
944 void
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);
949 #if CX_DEBUG
950 fprintf(stderr, "current timer time: %f, %f\n",
951 timer_struct->birthtime + timer_struct->duration,
952 Parrot_floatval_time());
953 #endif
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.
966 =cut
970 void
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);
983 =back
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.
990 =over 4
993 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
994 opcode_t *next)>
996 Add a sleep timer to the scheduler. This function is called by the C<sleep>
997 opcode.
999 =cut
1003 PARROT_EXPORT
1004 PARROT_WARN_UNUSED_RESULT
1005 PARROT_CAN_RETURN_NULL
1006 opcode_t *
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;
1012 Parrot_mutex lock;
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
1017 * pending tasks. */
1018 Parrot_cx_runloop_wake(interp, interp->scheduler);
1020 /* Tell this thread to sleep for the requested time. */
1021 COND_INIT(condition);
1022 MUTEX_INIT(lock);
1023 LOCK(lock);
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);
1027 UNLOCK(lock);
1028 COND_DESTROY(condition);
1029 MUTEX_DESTROY(lock);
1030 #else
1031 /* A more primitive, platform-specific, non-threaded form of sleep. */
1032 if (time > 1000) {
1033 /* prevent integer overflow when converting to microseconds */
1034 const int seconds = floor(time);
1035 Parrot_sleep(seconds);
1036 time -= seconds;
1038 Parrot_usleep((UINTVAL) time*1000000);
1039 #endif
1040 return next;
1046 =back
1048 =head2 Internal Functions
1050 Functions that are only used within the scheduler.
1052 =over 4
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.
1059 =cut
1063 static void
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);
1074 if (tid > 0) {
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);
1080 else {
1081 /* Move the timer to the active task list if the timer has
1082 * completed. */
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);
1098 =over 4
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.
1105 =cut
1109 static void
1110 scheduler_process_messages(PARROT_INTERP, ARGMOD(PMC *scheduler))
1112 ASSERT_ARGS(scheduler_process_messages)
1113 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(scheduler);
1115 PMC *message;
1116 STRING *suspend_str = CONST_STRING(interp, "suspend_for_gc");
1118 #if CX_DEBUG
1119 fprintf(stderr, "processing messages [interp=%p]\n", interp);
1120 #endif
1122 while (VTABLE_elements(interp, sched_struct->messages) > 0) {
1123 #if CX_DEBUG
1124 fprintf(stderr, "locking msg_lock (process) [interp=%p]\n", interp);
1125 #endif
1126 LOCK(sched_struct->msg_lock);
1127 message = VTABLE_pop_pmc(interp, sched_struct->messages);
1128 #if CX_DEBUG
1129 fprintf(stderr, "unlocking msg_lock (process) [interp=%p]\n", interp);
1130 #endif
1131 UNLOCK(sched_struct->msg_lock);
1132 if (!PMC_IS_NULL(message)
1133 && Parrot_str_equal(interp, VTABLE_get_string(interp, message),
1134 suspend_str)) {
1135 #if CX_DEBUG
1136 fprintf(stderr, "found a suspend, suspending [interp=%p]\n", interp);
1137 #endif
1138 pt_suspend_self_for_gc(interp);
1146 =back
1148 =cut
1154 * Local variables:
1155 * c-file-style: "parrot"
1156 * End:
1157 * vim: expandtab shiftwidth=4: