fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / pmc / scheduler.pmc
blob14b7c0ef0b58153647e2b711cc16f0da95ac0e86
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/scheduler.pmc - The concurrency scheduler
9 =head1 DESCRIPTION
11 Implements the core concurrency scheduler.
13 =head2 Vtable Functions
15 =over 4
17 =cut
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
32                                      lookup). */
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. */
45 =item C<void init()>
47 Initializes a concurrency scheduler object.
49 =cut
53     VTABLE void init() {
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. */
62         core_struct->id          = 0;
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);
71     }
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:
80 =over 4
82 =item C<id>
84 An C<Integer> representing the unique identifier for this scheduler.
86 =back
88 =cut
92     VTABLE void init_pmc(PMC *data) {
93         PMC              *elem;
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");
100         SELF.init();
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);
107     }
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.
117 =cut
121     void push_pmc(PMC *task) {
122         Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
123         STRING                  *task_id_str;
124         INTVAL                   new_tid;
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,
134                                          task_id_str, task);
136         if (task->vtable->base_type == enum_class_Timer)
137             VTABLE_push_integer(INTERP, core_struct->wait_index, new_tid);
138         else
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);
145     }
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.
155 =cut
159     VTABLE PMC *pop_pmc() {
160         Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
161         PMC *task = PMCNULL;
163         /* Pull the next valid task off the task list, skipping expired and
164          * deleted tasks. */
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);
169             if (tid > 0)
170                 task = VTABLE_get_pmc_keyed_int(INTERP,
171                             core_struct->task_list, tid);
172         }
174         return task;
175     }
180 =item C<INTVAL get_integer()>
182 Retrieves the number of pending tasks in the scheduler's task list.
184 =cut
188     VTABLE INTVAL get_integer() {
189         Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
190         return VTABLE_elements(INTERP, core_struct->task_index);
191     }
196 =item C<void delete_keyed_int(INTVAL key)>
198 Removes the task with the given task ID from the task list.
200 =cut
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);
210     }
215 =item C<PMC *share_ro()>
217 Sets this PMC as shared.
219 =cut
223     VTABLE PMC *share_ro() {
224         PMC              *shared_self;
225         Parrot_Scheduler_attributes *sched;
227         if (PObj_is_PMC_shared_TEST(SELF))
228             return 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);
239         return shared_self;
240     }
245 =item C<void destroy()>
247 Frees the scheduler's underlying struct.
249 =cut
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); */
259     }
264 =item C<void mark()>
266 Marks any referenced strings and PMCs as live.
268 =cut
271     VTABLE void mark() {
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);
280         }
281     }
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>).
292 =cut
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);
302     }
307 =item C<void freeze(PMC *info)>
309 Archives the scheduler.
311 =cut
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);
323     }
328 =item C<void thaw(PMC *info)>
330 Unarchives the scheduler.
332 =cut
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. */
344         SELF.init();
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;
351     }
356 =item C<void thawfinish(PMC *info)>
358 Finishes thawing the scheduler.
360 =cut
364     VTABLE void thawfinish(PMC *info) {
365         Parrot_cx_refresh_task_list(INTERP, SELF);
366     }
371 =back
373 =head2 Methods
375 =over 4
377 =cut
383 =item C<METHOD add_handler(PMC *handler)>
385 Adds a handler to the scheduler.
387 =cut
391     METHOD add_handler(PMC *handler) {
392         Parrot_Scheduler_attributes *core_struct = PARROT_SCHEDULER(SELF);
393         VTABLE_unshift_pmc(INTERP, core_struct->handlers, handler);
394     }
399 =item C<METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag)>
401 Deletes a handler from the scheduler.
403 =cut
407     METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag) {
408         PMC    *handlers;
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);
416         if (!have_type)
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);
426                     RETURN(void);
427                 }
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);
431                     RETURN(void);
432                }
433             }
434         }
436         Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
437             "No handler to delete.");
438     }
443 =item C<METHOD find_handler(PMC *task)>
445 Searchs for a handler for the given task. If no handler is found, returns
446 PMCNULL.
448 =cut
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");
455         PMC    *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);
462         }
463         else {
464             PMC *handlers;
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);
470         }
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);
479                 if (valid_handler) {
480                     if (task->vtable->base_type == enum_class_Exception)
481                         VTABLE_set_integer_native(INTERP, handler, 1);
482                     RETURN(PMC *handler);
483                 }
484             }
486         }
488         RETURN(PMC *PMCNULL);
489     }
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.
500 =cut
504     METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag) {
505         /* avoid uninitialized value warning */
506         PMC   *handlers = NULL;
507         INTVAL elements;
508         INTVAL count    = 0;
509         INTVAL index;
511         GET_ATTR_handlers(INTERP, SELF, handlers);
512         elements = VTABLE_elements(INTERP, handlers);
514         if (!have_type)
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))
527                         ++count;
528             }
529         }
531         RETURN(INTVAL count);
532     }
537 =back
539 =head1 SEE ALSO
541 F<docs/pdds/pdd25_concurrency.pod>.
543 =cut
548  * Local variables:
549  *   c-file-style: "parrot"
550  * End:
551  * vim: expandtab shiftwidth=4:
552  */