Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: tt855_debuggin…
Fetching contributors…

Cannot retrieve contributors at this time

552 lines (362 sloc) 14.443 kb
/*
Copyright (C) 2001-2010, Parrot Foundation.
=head1 NAME
src/pmc/scheduler.pmc - The concurrency scheduler
=head1 DESCRIPTION
Implements the core concurrency scheduler.
=head2 Vtable Functions
=over 4
=cut
*/
#include "parrot/scheduler_private.h"
/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */
pmclass Scheduler auto_attrs {
ATTR INTVAL id; /* The scheduler's ID. */
ATTR INTVAL max_tid; /* The highest assigned task ID. */
ATTR INTVAL pending; /* A count of pending tasks (cached for fast
lookup). */
ATTR PMC *task_list; /* The current list of tasks. */
ATTR PMC *task_index; /* An index into the current list of tasks,
ordered by priority. */
ATTR PMC *wait_index; /* An unordered index of inactive tasks. */
ATTR PMC *handlers; /* The list of currently active handlers. */
ATTR PMC *messages; /* A message queue used for communication
between schedulers. */
ATTR Parrot_mutex msg_lock; /* Lock to synchronize the message queue. */
ATTR Parrot_Interp interp; /* A link to the scheduler's interpreter. */
/*
=item C<void init()>
Initializes a concurrency scheduler object.
=cut
*/
VTABLE void init() {
Parrot_Scheduler_attributes * const core_struct =
(Parrot_Scheduler_attributes *) PMC_data(SELF);
/* Set flags for custom GC mark and destroy. */
PObj_custom_mark_SET(SELF);
PObj_custom_destroy_SET(SELF);
/* Set up the core struct. */
core_struct->id = 0;
core_struct->max_tid = 0;
core_struct->task_list = Parrot_pmc_new(INTERP, enum_class_Hash);
core_struct->task_index = Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray);
core_struct->wait_index = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
core_struct->handlers = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
core_struct->messages = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
core_struct->interp = INTERP;
MUTEX_INIT(core_struct->msg_lock);
}
/*
=item C<void init_pmc(PMC *data)>
Initializes a new Scheduler with a C<Hash> PMC with any or all of the keys:
=over 4
=item C<id>
An C<Integer> representing the unique identifier for this scheduler.
=back
=cut
*/
VTABLE void init_pmc(PMC *data) {
PMC *elem;
Parrot_Scheduler_attributes *core_struct;
if (!VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Hash")))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
"Scheduler initializer must be a Hash");
SELF.init();
core_struct = PARROT_SCHEDULER(SELF);
elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "id"));
if (!PMC_IS_NULL(elem))
core_struct->id = VTABLE_get_integer(INTERP, elem);
}
/*
=item C<void push_pmc(PMC *value)>
Inserts a task into the task list, giving it a task ID one higher than the
current maximum, and a birthtime of the current time.
=cut
*/
void push_pmc(PMC *task) {
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
STRING *task_id_str;
INTVAL new_tid;
task = VTABLE_share_ro(INTERP, task);
VTABLE_set_number_native(INTERP, task, Parrot_floatval_time());
new_tid = ++(core_struct->max_tid);
VTABLE_set_integer_native(INTERP, task, new_tid);
task_id_str = Parrot_str_from_int(INTERP, new_tid);
VTABLE_set_pmc_keyed_str(INTERP, core_struct->task_list,
task_id_str, task);
if (task->vtable->base_type == enum_class_Timer)
VTABLE_push_integer(INTERP, core_struct->wait_index, new_tid);
else
VTABLE_push_integer(INTERP, core_struct->task_index, new_tid);
SCHEDULER_cache_valid_CLEAR(SELF);
if (task->vtable->base_type != enum_class_Exception)
Parrot_cx_runloop_wake(core_struct->INTERP, SELF);
}
/*
=item C<PMC *pop_pmc()>
Retrieves the next task from the task list. If the task index is invalid,
recalculates it before retrieving the next task.
=cut
*/
VTABLE PMC *pop_pmc() {
Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
PMC *task = PMCNULL;
/* Pull the next valid task off the task list, skipping expired and
* deleted tasks. */
while (PMC_IS_NULL(task)
&& VTABLE_elements(INTERP, core_struct->task_index) > 0) {
const INTVAL tid = VTABLE_shift_integer(INTERP, core_struct->task_index);
if (tid > 0)
task = VTABLE_get_pmc_keyed_int(INTERP,
core_struct->task_list, tid);
}
return task;
}
/*
=item C<INTVAL get_integer()>
Retrieves the number of pending tasks in the scheduler's task list.
=cut
*/
VTABLE INTVAL get_integer() {
Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
return VTABLE_elements(INTERP, core_struct->task_index);
}
/*
=item C<void delete_keyed_int(INTVAL key)>
Removes the task with the given task ID from the task list.
=cut
*/
VTABLE void delete_keyed_int(INTVAL key) {
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
STRING * const task_id_str = Parrot_str_from_int(INTERP, key);
VTABLE_delete_keyed_str(INTERP, core_struct->task_list, task_id_str);
SCHEDULER_cache_valid_CLEAR(SELF);
}
/*
=item C<PMC *share_ro()>
Sets this PMC as shared.
=cut
*/
VTABLE PMC *share_ro() {
PMC *shared_self;
Parrot_Scheduler_attributes *sched;
if (PObj_is_PMC_shared_TEST(SELF))
return SELF;
shared_self = pt_shared_fixup(INTERP, SELF);
sched = PARROT_SCHEDULER(shared_self);
sched->task_list = pt_shared_fixup(INTERP, sched->task_list);
sched->task_index = pt_shared_fixup(INTERP, sched->task_index);
sched->wait_index = pt_shared_fixup(INTERP, sched->wait_index);
sched->handlers = pt_shared_fixup(INTERP, sched->handlers);
sched->messages = pt_shared_fixup(INTERP, sched->messages);
return shared_self;
}
/*
=item C<void destroy()>
Frees the scheduler's underlying struct.
=cut
*/
VTABLE void destroy() {
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
core_struct->interp->scheduler = NULL;
/* TT #946: this line is causing an order-of-destruction error
because the scheduler is being freed before its tasks.
Commenting this out till we get a real fix (although it's a hack) */
/* MUTEX_DESTROY(core_struct->msg_lock); */
}
/*
=item C<void mark()>
Marks any referenced strings and PMCs as live.
=cut
*/
VTABLE void mark() {
if (PARROT_SCHEDULER(SELF)) {
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_list);
Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_index);
Parrot_gc_mark_PMC_alive(INTERP, core_struct->wait_index);
Parrot_gc_mark_PMC_alive(INTERP, core_struct->handlers);
Parrot_gc_mark_PMC_alive(INTERP, core_struct->messages);
}
}
/*
=item C<void visit(PMC *info)>
Visits the contents of the scheduler (used by freeze/thaw).
C<*info> is the visit info (see F<include/parrot/pmc_freeze.h>).
=cut
*/
VTABLE void visit(PMC *info) {
/* 1) visit task list */
VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, task_list);
/* 2) visit the handlers */
VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, handlers);
}
/*
=item C<void freeze(PMC *info)>
Archives the scheduler.
=cut
*/
VTABLE void freeze(PMC *info) {
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
/* 1) freeze scheduler id */
VTABLE_push_integer(INTERP, info, core_struct->id);
/* 2) freeze maximum task id */
VTABLE_push_integer(INTERP, info, core_struct->max_tid);
}
/*
=item C<void thaw(PMC *info)>
Unarchives the scheduler.
=cut
*/
VTABLE void thaw(PMC *info) {
/* 1. thaw scheduler id */
const INTVAL id = VTABLE_shift_integer(INTERP, info);
/* 2. thaw maximum task id */
const INTVAL max_tid = VTABLE_shift_integer(INTERP, info);
/* Allocate the scheduler's core data struct and set custom flags. */
SELF.init();
/* Set the scheduler's id to the frozen id */
PARROT_SCHEDULER(SELF)->id = id;
/* Set the scheduler's maximum task id to the frozen tid */
PARROT_SCHEDULER(SELF)->max_tid = max_tid;
}
/*
=item C<void thawfinish(PMC *info)>
Finishes thawing the scheduler.
=cut
*/
VTABLE void thawfinish(PMC *info) {
Parrot_cx_refresh_task_list(INTERP, SELF);
}
/*
=back
=head2 Methods
=over 4
=cut
*/
/*
=item C<METHOD add_handler(PMC *handler)>
Adds a handler to the scheduler.
=cut
*/
METHOD add_handler(PMC *handler) {
Parrot_Scheduler_attributes *core_struct = PARROT_SCHEDULER(SELF);
VTABLE_unshift_pmc(INTERP, core_struct->handlers, handler);
}
/*
=item C<METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag)>
Deletes a handler from the scheduler.
=cut
*/
METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag) {
PMC *handlers;
INTVAL elements, index;
STRING * const except_str = CONST_STRING(INTERP, "exception");
STRING * const event_str = CONST_STRING(INTERP, "event");
GET_ATTR_handlers(INTERP, SELF, handlers);
elements = VTABLE_elements(INTERP, handlers);
if (!have_type)
VTABLE_shift_pmc(INTERP, handlers);
/* Loop from newest handler to oldest handler. */
for (index = 0; index < elements; ++index) {
const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
if (!PMC_IS_NULL(handler)) {
if (STRING_equal(INTERP, type, except_str)
&& handler->vtable->base_type == enum_class_ExceptionHandler) {
VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
RETURN(void);
}
else if (STRING_equal(INTERP, type, event_str)
&& handler->vtable->base_type == enum_class_EventHandler) {
VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
RETURN(void);
}
}
}
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
"No handler to delete.");
}
/*
=item C<METHOD find_handler(PMC *task)>
Searchs for a handler for the given task. If no handler is found, returns
PMCNULL.
=cut
*/
METHOD find_handler(PMC *task) {
STRING * const handled_str = CONST_STRING(INTERP, "handled");
STRING * const iter_str = CONST_STRING(INTERP, "handler_iter");
PMC *iter;
/* Exceptions store the handler iterator for rethrow, other kinds of
* tasks don't (though they could). */
if (task->vtable->base_type == enum_class_Exception
&& VTABLE_get_integer_keyed_str(INTERP, task, handled_str) == -1) {
iter = VTABLE_get_attr_str(INTERP, task, iter_str);
}
else {
PMC *handlers;
GET_ATTR_handlers(INTERP, SELF, handlers);
iter = VTABLE_get_iter(INTERP, handlers);
if (task->vtable->base_type == enum_class_Exception)
VTABLE_set_attr_str(INTERP, task, iter_str, iter);
}
/* Loop from newest handler to oldest handler. */
while (VTABLE_get_bool(INTERP, iter)) {
PMC * const handler = VTABLE_shift_pmc(INTERP, iter);
INTVAL valid_handler = 0;
if (!PMC_IS_NULL(handler)) {
(const INTVAL valid_handler) = PCCINVOKE(INTERP, handler, "can_handle", PMC *task);
if (valid_handler) {
if (task->vtable->base_type == enum_class_Exception)
VTABLE_set_integer_native(INTERP, handler, 1);
RETURN(PMC *handler);
}
}
}
RETURN(PMC *PMCNULL);
}
/*
=item C<METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag)>
Returns the number of handlers currently held by the scheduler. If a type
argument is passed, only counts handlers of that type (C<event>, C<exception>).
If no type argument is passed, counts all handlers.
=cut
*/
METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag) {
/* avoid uninitialized value warning */
PMC *handlers = NULL;
INTVAL elements;
INTVAL count = 0;
INTVAL index;
GET_ATTR_handlers(INTERP, SELF, handlers);
elements = VTABLE_elements(INTERP, handlers);
if (!have_type)
RETURN(INTVAL elements);
for (index = 0; index < elements; ++index) {
const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
STRING * const exception = CONST_STRING(INTERP, "exception");
STRING * const event = CONST_STRING(INTERP, "event");
if (!PMC_IS_NULL(handler)) {
if ((STRING_equal(INTERP, type, exception)
&& handler->vtable->base_type == enum_class_ExceptionHandler)
|| (STRING_equal(INTERP, type, event)
&& handler->vtable->base_type == enum_class_EventHandler))
++count;
}
}
RETURN(INTVAL count);
}
}
/*
=back
=head1 SEE ALSO
F<docs/pdds/pdd25_concurrency.pod>.
=cut
*/
/*
* Local variables:
* c-file-style: "parrot"
* End:
* vim: expandtab shiftwidth=4:
*/
Jump to Line
Something went wrong with that request. Please try again.