Permalink
Newer
Older
100644 552 lines (362 sloc) 14.1 KB
Mar 18, 2010
2
Copyright (C) 2001-2010, Parrot Foundation.
3
4
=head1 NAME
5
6
src/pmc/scheduler.pmc - The concurrency scheduler
7
8
=head1 DESCRIPTION
9
10
Implements the core concurrency scheduler.
11
13
14
=over 4
15
16
=cut
17
18
*/
19
20
#include "parrot/scheduler_private.h"
22
/* HEADERIZER HFILE: none */
23
/* HEADERIZER BEGIN: static */
24
/* HEADERIZER END: static */
25
26
pmclass Scheduler auto_attrs {
28
ATTR INTVAL id; /* The scheduler's ID. */
29
ATTR INTVAL max_tid; /* The highest assigned task ID. */
30
ATTR INTVAL pending; /* A count of pending tasks (cached for fast
31
lookup). */
32
ATTR PMC *task_list; /* The current list of tasks. */
33
ATTR PMC *task_index; /* An index into the current list of tasks,
34
ordered by priority. */
35
ATTR PMC *wait_index; /* An unordered index of inactive tasks. */
36
ATTR PMC *handlers; /* The list of currently active handlers. */
37
ATTR PMC *messages; /* A message queue used for communication
38
between schedulers. */
39
ATTR Parrot_mutex msg_lock; /* Lock to synchronize the message queue. */
40
ATTR Parrot_Interp interp; /* A link to the scheduler's interpreter. */
41
42
/*
43
44
=item C<void init()>
45
46
Initializes a concurrency scheduler object.
53
Parrot_Scheduler_attributes * const core_struct =
54
(Parrot_Scheduler_attributes *) PMC_data(SELF);
56
/* Set flags for custom GC mark and destroy. */
59
60
/* Set up the core struct. */
61
core_struct->id = 0;
62
core_struct->max_tid = 0;
63
core_struct->task_list = Parrot_pmc_new(INTERP, enum_class_Hash);
64
core_struct->task_index = Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray);
65
core_struct->wait_index = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
66
core_struct->handlers = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
67
core_struct->messages = Parrot_pmc_new(interp, enum_class_ResizablePMCArray);
69
MUTEX_INIT(core_struct->msg_lock);
75
=item C<void init_pmc(PMC *data)>
76
77
Initializes a new Scheduler with a C<Hash> PMC with any or all of the keys:
78
79
=over 4
80
81
=item C<id>
82
83
An C<Integer> representing the unique identifier for this scheduler.
84
85
=back
86
91
VTABLE void init_pmc(PMC *data) {
93
Parrot_Scheduler_attributes *core_struct;
95
if (!VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Hash")))
96
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
97
"Scheduler initializer must be a Hash");
101
core_struct = PARROT_SCHEDULER(SELF);
102
elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "id"));
104
if (!PMC_IS_NULL(elem))
105
core_struct->id = VTABLE_get_integer(INTERP, elem);
111
=item C<void push_pmc(PMC *value)>
112
113
Inserts a task into the task list, giving it a task ID one higher than the
114
current maximum, and a birthtime of the current time.
121
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
122
STRING *task_id_str;
123
INTVAL new_tid;
125
task = VTABLE_share_ro(INTERP, task);
126
VTABLE_set_number_native(INTERP, task, Parrot_floatval_time());
128
new_tid = ++(core_struct->max_tid);
129
VTABLE_set_integer_native(INTERP, task, new_tid);
130
task_id_str = Parrot_str_from_int(INTERP, new_tid);
132
VTABLE_set_pmc_keyed_str(INTERP, core_struct->task_list,
133
task_id_str, task);
135
if (task->vtable->base_type == enum_class_Timer)
136
VTABLE_push_integer(INTERP, core_struct->wait_index, new_tid);
138
VTABLE_push_integer(INTERP, core_struct->task_index, new_tid);
139
140
SCHEDULER_cache_valid_CLEAR(SELF);
141
142
if (task->vtable->base_type != enum_class_Exception)
143
Parrot_cx_runloop_wake(core_struct->INTERP, SELF);
147
/*
148
149
=item C<PMC *pop_pmc()>
150
151
Retrieves the next task from the task list. If the task index is invalid,
152
recalculates it before retrieving the next task.
159
Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
162
/* Pull the next valid task off the task list, skipping expired and
163
* deleted tasks. */
164
while (PMC_IS_NULL(task)
165
&& VTABLE_elements(INTERP, core_struct->task_index) > 0) {
Mar 18, 2010
166
const INTVAL tid = VTABLE_shift_integer(INTERP, core_struct->task_index);
169
task = VTABLE_get_pmc_keyed_int(INTERP,
170
core_struct->task_list, tid);
179
=item C<INTVAL get_integer()>
180
181
Retrieves the number of pending tasks in the scheduler's task list.
188
Parrot_Scheduler_attributes * core_struct = PARROT_SCHEDULER(SELF);
189
return VTABLE_elements(INTERP, core_struct->task_index);
190
}
191
193
/*
194
195
=item C<void delete_keyed_int(INTVAL key)>
196
197
Removes the task with the given task ID from the task list.
198
199
=cut
200
201
*/
202
203
VTABLE void delete_keyed_int(INTVAL key) {
Mar 18, 2010
204
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
205
STRING * const task_id_str = Parrot_str_from_int(INTERP, key);
206
207
VTABLE_delete_keyed_str(INTERP, core_struct->task_list, task_id_str);
208
SCHEDULER_cache_valid_CLEAR(SELF);
209
}
210
224
Parrot_Scheduler_attributes *sched;
225
226
if (PObj_is_PMC_shared_TEST(SELF))
227
return SELF;
228
229
shared_self = pt_shared_fixup(INTERP, SELF);
230
sched = PARROT_SCHEDULER(shared_self);
232
sched->task_list = pt_shared_fixup(INTERP, sched->task_list);
233
sched->task_index = pt_shared_fixup(INTERP, sched->task_index);
234
sched->wait_index = pt_shared_fixup(INTERP, sched->wait_index);
235
sched->handlers = pt_shared_fixup(INTERP, sched->handlers);
236
sched->messages = pt_shared_fixup(INTERP, sched->messages);
237
238
return shared_self;
239
}
240
246
Frees the scheduler's underlying struct.
252
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
253
core_struct->interp->scheduler = NULL;
254
/* TT #946: this line is causing an order-of-destruction error
255
because the scheduler is being freed before its tasks.
256
Commenting this out till we get a real fix (although it's a hack) */
257
/* MUTEX_DESTROY(core_struct->msg_lock); */
261
/*
262
263
=item C<void mark()>
264
265
Marks any referenced strings and PMCs as live.
271
if (PARROT_SCHEDULER(SELF)) {
272
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
274
Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_list);
275
Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_index);
276
Parrot_gc_mark_PMC_alive(INTERP, core_struct->wait_index);
277
Parrot_gc_mark_PMC_alive(INTERP, core_struct->handlers);
278
Parrot_gc_mark_PMC_alive(INTERP, core_struct->messages);
285
=item C<void visit(PMC *info)>
287
Visits the contents of the scheduler (used by freeze/thaw).
289
C<*info> is the visit info (see F<include/parrot/pmc_freeze.h>).
295
VTABLE void visit(PMC *info) {
297
VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, task_list);
298
299
/* 2) visit the handlers */
300
VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, handlers);
306
=item C<void freeze(PMC *info)>
314
VTABLE void freeze(PMC *info) {
315
Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
316
317
/* 1) freeze scheduler id */
318
VTABLE_push_integer(INTERP, info, core_struct->id);
319
320
/* 2) freeze maximum task id */
321
VTABLE_push_integer(INTERP, info, core_struct->max_tid);
327
=item C<void thaw(PMC *info)>
335
VTABLE void thaw(PMC *info) {
337
const INTVAL id = VTABLE_shift_integer(INTERP, info);
338
339
/* 2. thaw maximum task id */
340
const INTVAL max_tid = VTABLE_shift_integer(INTERP, info);
341
342
/* Allocate the scheduler's core data struct and set custom flags. */
343
SELF.init();
344
345
/* Set the scheduler's id to the frozen id */
346
PARROT_SCHEDULER(SELF)->id = id;
347
348
/* Set the scheduler's maximum task id to the frozen tid */
349
PARROT_SCHEDULER(SELF)->max_tid = max_tid;
350
}
351
355
=item C<void thawfinish(PMC *info)>
363
VTABLE void thawfinish(PMC *info) {
364
Parrot_cx_refresh_task_list(INTERP, SELF);
368
/*
369
370
=back
371
372
=head2 Methods
373
374
=over 4
375
376
=cut
377
378
*/
379
380
/*
381
382
=item C<METHOD add_handler(PMC *handler)>
390
METHOD add_handler(PMC *handler) {
391
Parrot_Scheduler_attributes *core_struct = PARROT_SCHEDULER(SELF);
392
VTABLE_unshift_pmc(INTERP, core_struct->handlers, handler);
393
}
394
398
=item C<METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag)>
400
Deletes a handler from the scheduler.
406
METHOD delete_handler(STRING *type :optional, INTVAL have_type :opt_flag) {
407
PMC *handlers;
408
INTVAL elements, index;
Mar 18, 2010
409
STRING * const except_str = CONST_STRING(INTERP, "exception");
410
STRING * const event_str = CONST_STRING(INTERP, "event");
411
412
GET_ATTR_handlers(INTERP, SELF, handlers);
413
elements = VTABLE_elements(INTERP, handlers);
414
416
VTABLE_shift_pmc(INTERP, handlers);
417
418
/* Loop from newest handler to oldest handler. */
419
for (index = 0; index < elements; ++index) {
Mar 18, 2010
420
const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
422
if (STRING_equal(INTERP, type, except_str)
423
&& handler->vtable->base_type == enum_class_ExceptionHandler) {
424
VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
425
RETURN(void);
426
}
427
else if (STRING_equal(INTERP, type, event_str)
428
&& handler->vtable->base_type == enum_class_EventHandler) {
429
VTABLE_set_pmc_keyed_int(INTERP, handlers, index, PMCNULL);
430
RETURN(void);
431
}
432
}
433
}
434
435
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
442
=item C<METHOD find_handler(PMC *task)>
444
Searchs for a handler for the given task. If no handler is found, returns
445
PMCNULL.
446
447
=cut
448
449
*/
450
451
METHOD find_handler(PMC *task) {
452
STRING * const handled_str = CONST_STRING(INTERP, "handled");
453
STRING * const iter_str = CONST_STRING(INTERP, "handler_iter");
455
456
/* Exceptions store the handler iterator for rethrow, other kinds of
457
* tasks don't (though they could). */
458
if (task->vtable->base_type == enum_class_Exception
459
&& VTABLE_get_integer_keyed_str(INTERP, task, handled_str) == -1) {
460
iter = VTABLE_get_attr_str(INTERP, task, iter_str);
461
}
462
else {
463
PMC *handlers;
464
GET_ATTR_handlers(INTERP, SELF, handlers);
465
iter = VTABLE_get_iter(INTERP, handlers);
466
467
if (task->vtable->base_type == enum_class_Exception)
468
VTABLE_set_attr_str(INTERP, task, iter_str, iter);
469
}
470
471
/* Loop from newest handler to oldest handler. */
472
while (VTABLE_get_bool(INTERP, iter)) {
Mar 18, 2010
473
PMC * const handler = VTABLE_shift_pmc(INTERP, iter);
474
475
INTVAL valid_handler = 0;
476
if (!PMC_IS_NULL(handler)) {
Mar 18, 2010
477
(const INTVAL valid_handler) = PCCINVOKE(INTERP, handler, "can_handle", PMC *task);
479
if (task->vtable->base_type == enum_class_Exception)
480
VTABLE_set_integer_native(INTERP, handler, 1);
493
=item C<METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag)>
495
Returns the number of handlers currently held by the scheduler. If a type
496
argument is passed, only counts handlers of that type (C<event>, C<exception>).
497
If no type argument is passed, counts all handlers.
503
METHOD count_handlers(STRING *type :optional, INTVAL have_type :opt_flag) {
504
/* avoid uninitialized value warning */
505
PMC *handlers = NULL;
506
INTVAL elements;
507
INTVAL count = 0;
508
INTVAL index;
509
510
GET_ATTR_handlers(INTERP, SELF, handlers);
511
elements = VTABLE_elements(INTERP, handlers);
513
if (!have_type)
514
RETURN(INTVAL elements);
515
516
for (index = 0; index < elements; ++index) {
Mar 18, 2010
517
const PMC * const handler = VTABLE_get_pmc_keyed_int(INTERP, handlers, index);
518
STRING * const exception = CONST_STRING(INTERP, "exception");
519
STRING * const event = CONST_STRING(INTERP, "event");
522
if ((STRING_equal(INTERP, type, exception)
523
&& handler->vtable->base_type == enum_class_ExceptionHandler)
524
|| (STRING_equal(INTERP, type, event)
525
&& handler->vtable->base_type == enum_class_EventHandler))
526
++count;
527
}
528
}
529
530
RETURN(INTVAL count);
531
}
532
}
533
534
/*
535
536
=back
537
538
=head1 SEE ALSO
539
540
F<docs/pdds/pdd25_concurrency.pod>.
541
542
=cut
543
544
*/
545
546
/*
547
* Local variables:
548
* c-file-style: "parrot"
549
* End:
550
* vim: expandtab shiftwidth=4 cinoptions='\:2=2' :