[t][TT #1119] Convert t/op/bitwise.t to PIR
[parrot.git] / src / scheduler.c
blob391846ce1bad805091ddd1cdf7b15dd80ce5dc89
1 /*
2 Copyright (C) 2007-2009, 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"
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"
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 = 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 = 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 *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 *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.
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, 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
496 *handler_type)>
498 Remove the top task handler of a particular type from the context's list of
499 handlers.
501 =cut
505 PARROT_EXPORT
506 void
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);
518 else {
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);
524 INTVAL index;
525 typedef enum { Hunknown, Hexception, Hevent } Htype;
527 const Htype htype =
528 Parrot_str_equal(interp, handler_type, exception_str) ?
529 Hexception :
530 Parrot_str_equal(interp, handler_type, event_str) ?
531 Hevent :
532 Hunknown;
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)) {
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 *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) ?
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)) {
613 switch (htype) {
614 case Hexception:
615 if (VTABLE_isa(interp, handler, handler_name))
616 count++;
617 break;
618 case Hevent:
619 if (handler->vtable->base_type == enum_class_EventHandler)
620 count++;
621 break;
622 default:
623 break;
627 return count;
634 =item C<void Parrot_cx_add_handler(PARROT_INTERP, PMC *handler)>
636 Add a task handler to scheduler's list of handlers.
638 =cut
642 PARROT_EXPORT
643 void
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
658 *handler_type)>
660 Remove the top task handler of a particular type from the scheduler's list of
661 handlers.
663 =cut
667 PARROT_EXPORT
668 void
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
682 *handler_type)>
684 Count the number of active handlers of a particular type (event, exception) in
685 the concurrency scheduler.
687 =cut
691 PARROT_EXPORT
692 INTVAL
693 Parrot_cx_count_handlers_typed(PARROT_INTERP, ARGIN(STRING *handler_type))
695 ASSERT_ARGS(Parrot_cx_count_handlers_typed)
696 INTVAL count = 0;
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);
704 return count;
709 =back
711 =head2 Scheduler Message Interface Functions
713 Functions that are used to interface with the message queue in the concurrency
714 scheduler.
716 =over 4
718 =item C<void Parrot_cx_send_message(PARROT_INTERP, STRING *messagetype, PMC
719 *payload)>
721 Send a message to a scheduler in a different interpreter/thread.
723 =cut
727 PARROT_EXPORT
728 void
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);
738 #if CX_DEBUG
739 fprintf(stderr, "sending message[interp=%p]\n", interp);
740 #endif
742 #if CX_DEBUG
743 fprintf(stderr, "locking msg_lock (send) [interp=%p]\n", interp);
744 #endif
745 LOCK(sched_struct->msg_lock);
746 VTABLE_push_pmc(interp, sched_struct->messages, message);
747 #if CX_DEBUG
748 fprintf(stderr, "unlocking msg_lock (send) [interp=%p]\n", interp);
749 #endif
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
760 *data)>
762 Send a message to the schedulers in all interpreters/threads linked to this
763 one.
765 =cut
769 PARROT_EXPORT
770 void
771 Parrot_cx_broadcast_message(PARROT_INTERP, ARGIN(STRING *messagetype), ARGIN_NULLOK(PMC *data))
773 ASSERT_ARGS(Parrot_cx_broadcast_message)
774 UINTVAL i;
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)
779 continue;
780 Parrot_cx_send_message(other_interp, messagetype, data);
782 UNLOCK(interpreter_array_mutex);
788 =back
790 =head2 Task Interface Functions
792 Functions that are used to interface with a specific task in the concurrency scheduler.
794 =over 4
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.
801 =cut
805 PARROT_EXPORT
806 PARROT_CAN_RETURN_NULL
807 PMC *
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;
812 #if CX_DEBUG
813 fprintf(stderr, "searching for handler\n");
814 #endif
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);
822 #if CX_DEBUG
823 fprintf(stderr, "done searching for handler\n");
824 #endif
826 return handler;
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.
836 =cut
840 PARROT_EXPORT
841 PARROT_CAN_RETURN_NULL
842 PMC *
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
850 * for a handler
852 static int already_doing = 0;
853 static PMC * keep_context = NULL;
855 PMC *context;
856 PMC *iter = PMCNULL;
857 STRING * const handled_str = CONST_STRING(interp, "handled");
858 STRING * const iter_str = CONST_STRING(interp, "handler_iter");
860 if (already_doing) {
861 Parrot_io_eprintf(interp,
862 "** Exception caught while looking for a handler, trying next **\n");
863 if (! keep_context)
864 return NULL;
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);
870 keep_context = NULL;
871 if (context && !PMC_IS_NULL(Parrot_pcc_get_handlers(interp, context)))
872 iter = VTABLE_get_iter(interp, Parrot_pcc_get_handlers(interp, context));
873 else
874 iter = PMCNULL;
876 else {
877 ++already_doing;
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);
886 else {
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));
893 while (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);
904 else
905 Parrot_pcc_invoke_method_from_c_args(interp, handler, CONST_STRING(interp, "can_handle"),
906 "P->I", task, &valid_handler);
908 if (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);
914 --already_doing;
915 keep_context = NULL;
916 return handler;
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));
925 else
926 iter = PMCNULL;
929 /* Reached the end of the context chain without finding a handler. */
931 --already_doing;
932 return PMCNULL;
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.
941 =cut
945 void
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);
950 #if CX_DEBUG
951 fprintf(stderr, "current timer time: %f, %f\n",
952 timer_struct->birthtime + timer_struct->duration,
953 Parrot_floatval_time());
954 #endif
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.
967 =cut
971 void
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);
984 =back
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.
991 =over 4
994 =item C<opcode_t * Parrot_cx_schedule_sleep(PARROT_INTERP, FLOATVAL time,
995 opcode_t *next)>
997 Add a sleep timer to the scheduler. This function is called by the C<sleep>
998 opcode.
1000 =cut
1004 PARROT_EXPORT
1005 PARROT_WARN_UNUSED_RESULT
1006 PARROT_CAN_RETURN_NULL
1007 opcode_t *
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;
1013 Parrot_mutex lock;
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
1018 * pending tasks. */
1019 Parrot_cx_runloop_wake(interp, interp->scheduler);
1021 /* Tell this thread to sleep for the requested time. */
1022 COND_INIT(condition);
1023 MUTEX_INIT(lock);
1024 LOCK(lock);
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);
1028 UNLOCK(lock);
1029 COND_DESTROY(condition);
1030 MUTEX_DESTROY(lock);
1031 #else
1032 /* A more primitive, platform-specific, non-threaded form of sleep. */
1033 if (time > 1000) {
1034 /* prevent integer overflow when converting to microseconds */
1035 const int seconds = floor(time);
1036 Parrot_sleep(seconds);
1037 time -= seconds;
1039 Parrot_usleep((UINTVAL) time*1000000);
1040 #endif
1041 return next;
1047 =back
1049 =head2 Internal Functions
1051 Functions that are only used within the scheduler.
1053 =over 4
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.
1060 =cut
1064 static void
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);
1075 if (tid > 0) {
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);
1081 else {
1082 /* Move the timer to the active task list if the timer has
1083 * completed. */
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);
1099 =over 4
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.
1106 =cut
1110 static void
1111 scheduler_process_messages(PARROT_INTERP, ARGMOD(PMC *scheduler))
1113 ASSERT_ARGS(scheduler_process_messages)
1114 Parrot_Scheduler_attributes * sched_struct = PARROT_SCHEDULER(scheduler);
1116 PMC *message;
1117 STRING *suspend_str = CONST_STRING(interp, "suspend_for_gc");
1119 #if CX_DEBUG
1120 fprintf(stderr, "processing messages [interp=%p]\n", interp);
1121 #endif
1123 while (VTABLE_elements(interp, sched_struct->messages) > 0) {
1124 #if CX_DEBUG
1125 fprintf(stderr, "locking msg_lock (process) [interp=%p]\n", interp);
1126 #endif
1127 LOCK(sched_struct->msg_lock);
1128 message = VTABLE_pop_pmc(interp, sched_struct->messages);
1129 #if CX_DEBUG
1130 fprintf(stderr, "unlocking msg_lock (process) [interp=%p]\n", interp);
1131 #endif
1132 UNLOCK(sched_struct->msg_lock);
1133 if (!PMC_IS_NULL(message)
1134 && Parrot_str_equal(interp, VTABLE_get_string(interp, message),
1135 suspend_str)) {
1136 #if CX_DEBUG
1137 fprintf(stderr, "found a suspend, suspending [interp=%p]\n", interp);
1138 #endif
1139 pt_suspend_self_for_gc(interp);
1147 =back
1149 =cut
1155 * Local variables:
1156 * c-file-style: "parrot"
1157 * End:
1158 * vim: expandtab shiftwidth=4: