2 Copyright (C) 2001-2010, Parrot Foundation.
7 src/pmc/scheduler.pmc - The concurrency scheduler
11 Implements the core concurrency scheduler.
13 =head2 Vtable Functions
21 #include "parrot/scheduler_private.h"
23 /* HEADERIZER HFILE: none */
24 /* HEADERIZER BEGIN: static */
25 /* HEADERIZER END: static */
27 pmclass Scheduler auto_attrs {
29 ATTR INTVAL id; /* The scheduler's ID. */
30 ATTR INTVAL max_tid; /* The highest assigned task ID. */
31 ATTR INTVAL pending; /* A count of pending tasks (cached for fast
33 ATTR PMC *task_list; /* The current list of tasks. */
34 ATTR PMC *task_index; /* An index into the current list of tasks,
35 ordered by priority. */
36 ATTR PMC *wait_index; /* An unordered index of inactive tasks. */
37 ATTR PMC *handlers; /* The list of currently active handlers. */
38 ATTR PMC *messages; /* A message queue used for communication
39 between schedulers. */
40 ATTR Parrot_mutex msg_lock; /* Lock to synchronize the message queue. */
41 ATTR Parrot_Interp interp; /* A link to the scheduler's interpreter. */
47 Initializes a concurrency scheduler object.
54 Parrot_Scheduler_attributes * const core_struct =
55 (Parrot_Scheduler_attributes *) PMC_data(SELF);
57 /* Set flags for custom GC mark and destroy. */
58 PObj_custom_mark_SET(SELF);
59 PObj_custom_destroy_SET(SELF);
61 /* Set up the core struct. */
63 core_struct->max_tid = 0;
64 core_struct->task_list = Parrot_pmc_new(INTERP, enum_class_Hash);
65 core_struct->task_index = Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray);
66 core_struct->wait_index = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
67 core_struct->handlers = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
68 core_struct->messages = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
69 core_struct->interp = INTERP;
70 MUTEX_INIT(core_struct->msg_lock);
76 =item C<void init_pmc(PMC *data)>
78 Initializes a new Scheduler with a C<Hash> PMC with any or all of the keys:
84 An C<Integer> representing the unique identifier for this scheduler.
92 VTABLE void init_pmc(PMC *data) {
94 Parrot_Scheduler_attributes *core_struct;
96 if (!VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Hash")))
97 Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
98 "Scheduler initializer must be a Hash");
102 core_struct = PARROT_SCHEDULER(SELF);
103 elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "id"));
105 if (!PMC_IS_NULL(elem))
106 core_struct->id = VTABLE_get_integer(INTERP, elem);
112 =item C<void push_pmc(PMC *value)>
114 Inserts a task into the task list, giving it a task ID one higher than the
115 current maximum, and a birthtime of the current time.
121 void push_pmc(PMC *task) {
122 Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
126 task = VTABLE_share_ro(INTERP, task);
127 VTABLE_set_number_native(INTERP, task, Parrot_floatval_time());
129 new_tid = ++(core_struct->max_tid);
130 VTABLE_set_integer_native(INTERP, task, new_tid);
131 task_id_str = Parrot_str_from_int(INTERP, new_tid);
133 VTABLE_set_pmc_keyed_str(INTERP, core_struct->task_list,
136 if (task->vtable->base_type == enum_class_Timer)
137 VTABLE_push_integer(INTERP, core_struct->wait_index, new_tid);
139 VTABLE_push_integer(INTERP, core_struct->task_index, new_tid);
141 SCHEDULER_cache_valid_CLEAR(SELF);
143 if (task->vtable->base_type != enum_class_Exception)
144 Parrot_cx_runloop_wake(core_struct->INTERP, SELF);
150 =item C<PMC *pop_pmc()>
152 Retrieves the next task from the task list. If the task index is invalid,
153 recalculates it before retrieving the next task.
159 VTABLE PMC *pop_pmc() {
160 Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
163 /* Pull the next valid task off the task list, skipping expired and
165 while (PMC_IS_NULL(task)
166 && VTABLE_elements(INTERP, core_struct->task_index) > 0) {
167 const INTVAL tid = VTABLE_shift_integer(INTERP, core_struct->task_index);
170 task = VTABLE_get_pmc_keyed_int(INTERP,
171 core_struct->task_list, tid);
180 =item C<INTVAL get_integer()>
182 Retrieves the number of pending tasks in the scheduler's task list.
188 VTABLE INTVAL get_integer() {
189 Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
190 return VTABLE_elements(INTERP, core_struct->task_index);
196 =item C<void delete_keyed_int(INTVAL key)>
198 Removes the task with the given task ID from the task list.
204 VTABLE void delete_keyed_int(INTVAL key) {
205 Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
206 STRING * const task_id_str = Parrot_str_from_int(INTERP, key);
208 VTABLE_delete_keyed_str(INTERP, core_struct->task_list, task_id_str);
209 SCHEDULER_cache_valid_CLEAR(SELF);
215 =item C<PMC *share_ro()>
217 Sets this PMC as shared.
223 VTABLE PMC *share_ro() {
225 Parrot_Scheduler_attributes *sched;
227 if (PObj_is_PMC_shared_TEST(SELF))
230 shared_self = pt_shared_fixup(INTERP, SELF);
231 sched = PARROT_SCHEDULER(shared_self);
233 sched->task_list = pt_shared_fixup(INTERP, sched->task_list);
234 sched->task_index = pt_shared_fixup(INTERP, sched->task_index);
235 sched->wait_index = pt_shared_fixup(INTERP, sched->wait_index);
236 sched->handlers = pt_shared_fixup(INTERP, sched->handlers);
237 sched->messages = pt_shared_fixup(INTERP, sched->messages);
245 =item C<void destroy()>
247 Frees the scheduler's underlying struct.
252 VTABLE void destroy() {
253 Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
254 core_struct->interp->scheduler = NULL;
255 /* TT #946: this line is causing an order-of-destruction error
256 because the scheduler is being freed before its tasks.
257 Commenting this out till we get a real fix (although it's a hack) */
258 /* MUTEX_DESTROY(core_struct->msg_lock); */
266 Marks any referenced strings and PMCs as live.
272 if (PARROT_SCHEDULER(SELF)) {
273 Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
275 Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_list);
276 Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_index);
277 Parrot_gc_mark_PMC_alive(INTERP, core_struct->wait_index);
278 Parrot_gc_mark_PMC_alive(INTERP, core_struct->handlers);
279 Parrot_gc_mark_PMC_alive(INTERP, core_struct->messages);
286 =item C<void visit(PMC *info)>
288 Visits the contents of the scheduler (used by freeze/thaw).
290 C<*info> is the visit info (see F<include/parrot/pmc_freeze.h>).
296 VTABLE void visit(PMC *info) {
297 /* 1) visit task list */
298 VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, task_list);
300 /* 2) visit the handlers */
301 VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, handlers);
307 =item C<void freeze(PMC *info)>
309 Archives the scheduler.
315 VTABLE void freeze(PMC *info) {
316 Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
318 /* 1) freeze scheduler id */
319 VTABLE_push_integer(INTERP, info, core_struct->id);
321 /* 2) freeze maximum task id */
322 VTABLE_push_integer(INTERP, info, core_struct->max_tid);
328 =item C<void thaw(PMC *info)>
330 Unarchives the scheduler.
336 VTABLE void thaw(PMC *info) {
337 /* 1. thaw scheduler id */
338 const INTVAL id = VTABLE_shift_integer(INTERP, info);
340 /* 2. thaw maximum task id */
341 const INTVAL max_tid = VTABLE_shift_integer(INTERP, info);
343 /* Allocate the scheduler's core data struct and set custom flags. */
346 /* Set the scheduler's id to the frozen id */
347 PARROT_SCHEDULER(SELF)->id = id;
349 /* Set the scheduler's maximum task id to the frozen tid */
350 PARROT_SCHEDULER(SELF)->max_tid = max_tid;
356 =item C<void thawfinish(PMC *info)>
358 Finishes thawing the scheduler.
364 VTABLE void thawfinish(PMC *info) {
365 Parrot_cx_refresh_task_list(INTERP, SELF);
383 =item C<METHOD add_handler(PMC *handler)>
385 Adds a handler to the scheduler.
391 METHOD add_handler(PMC *handler) {
392 Parrot_Scheduler_attributes *core_struct = PARROT_SCHEDULER(SELF);
393 VTABLE_unshift_pmc(INTERP, core_struct->handlers, handler);
399 =item C<METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag)>
401 Deletes a handler from the scheduler.
407 METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag) {
409 INTVAL elements, index;
410 STRING * const except_str = CONST_STRING(INTERP, "exception");
411 STRING * const event_str = CONST_STRING(INTERP, "event");
413 GET_ATTR_handlers(INTERP, SELF, handlers);
414 elements = VTABLE_elements(INTERP, handlers);
417 VTABLE_shift_pmc(INTERP, handlers);
419 /* Loop from newest handler to oldest handler. */
420 for (index = 0; index < elements; ++index) {
421 const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
422 if (!PMC_IS_NULL(handler)) {
423 if (Parrot_str_equal(INTERP, type, except_str)
424 && handler->vtable->base_type == enum_class_ExceptionHandler) {
425 VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
428 else if (Parrot_str_equal(INTERP, type, event_str)
429 && handler->vtable->base_type == enum_class_EventHandler) {
430 VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
436 Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
437 "No handler to delete.");
443 =item C<METHOD find_handler(PMC *task)>
445 Searchs for a handler for the given task. If no handler is found, returns
452 METHOD find_handler(PMC *task) {
453 STRING * const handled_str = CONST_STRING(INTERP, "handled");
454 STRING * const iter_str = CONST_STRING(INTERP, "handler_iter");
457 /* Exceptions store the handler iterator for rethrow, other kinds of
458 * tasks don't (though they could). */
459 if (task->vtable->base_type == enum_class_Exception
460 && VTABLE_get_integer_keyed_str(INTERP, task, handled_str) == -1) {
461 iter = VTABLE_get_attr_str(INTERP, task, iter_str);
465 GET_ATTR_handlers(INTERP, SELF, handlers);
466 iter = VTABLE_get_iter(INTERP, handlers);
468 if (task->vtable->base_type == enum_class_Exception)
469 VTABLE_set_attr_str(INTERP, task, iter_str, iter);
472 /* Loop from newest handler to oldest handler. */
473 while (VTABLE_get_bool(INTERP, iter)) {
474 PMC * const handler = VTABLE_shift_pmc(INTERP, iter);
476 INTVAL valid_handler = 0;
477 if (!PMC_IS_NULL(handler)) {
478 (const INTVAL valid_handler) = PCCINVOKE(INTERP, handler, "can_handle", PMC *task);
480 if (task->vtable->base_type == enum_class_Exception)
481 VTABLE_set_integer_native(INTERP, handler, 1);
482 RETURN(PMC *handler);
488 RETURN(PMC *PMCNULL);
494 =item C<METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag)>
496 Returns the number of handlers currently held by the scheduler. If a type
497 argument is passed, only counts handlers of that type (C<event>, C<exception>).
498 If no type argument is passed, counts all handlers.
504 METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag) {
505 /* avoid uninitialized value warning */
506 PMC *handlers = NULL;
511 GET_ATTR_handlers(INTERP, SELF, handlers);
512 elements = VTABLE_elements(INTERP, handlers);
515 RETURN(INTVAL elements);
517 for (index = 0; index < elements; ++index) {
518 const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
519 STRING * const exception = CONST_STRING(INTERP, "exception");
520 STRING * const event = CONST_STRING(INTERP, "event");
522 if (!PMC_IS_NULL(handler)) {
523 if ((Parrot_str_equal(INTERP, type, exception)
524 && handler->vtable->base_type == enum_class_ExceptionHandler)
525 || (Parrot_str_equal(INTERP, type, event)
526 && handler->vtable->base_type == enum_class_EventHandler))
531 RETURN(INTVAL count);
541 F<docs/pdds/pdd25_concurrency.pod>.
549 * c-file-style: "parrot"
551 * vim: expandtab shiftwidth=4: