1/*
2Copyright (C) 2001-2014, Parrot Foundation.
3
4=head1 NAME
5
6src/pmc/scheduler.pmc - Scheduler PMC
7
8=head1 DESCRIPTION
9
10Access to the core concurrency scheduler.
11
12=head2 Vtable Functions
13
14=over 4
15
16=cut
17
18*/
19
20#include "parrot/scheduler_private.h"
21
22/* HEADERIZER HFILE: none */
23/* HEADERIZER BEGIN: static */
24/* HEADERIZER END: static */
25
26pmclass Scheduler auto_attrs {
27    ATTR INTVAL        id;            /* The scheduler's ID. */
28    ATTR PMC          *handlers;      /* The list of currently active handlers. */
29    ATTR PMC          *messages;      /* A message queue used for communication
30                                         between schedulers. */
31
32    ATTR PMC          *task_queue;    /* List of tasks/green threads waiting to run */
33    ATTR PMC          *foreign_tasks; /* List of tasks/green threads waiting to run */
34    ATTR Parrot_mutex task_queue_lock;
35    ATTR PMC          *alarms;        /* List of future alarms ordered by time */
36
37    ATTR PMC          *all_tasks;     /* Hash of all active tasks by ID */
38    ATTR UINTVAL       next_task_id;  /* ID to assign to the next created task */
39
40    ATTR Parrot_Interp interp;        /* A link to the scheduler's interpreter. */
41
42/*
43
44=item C<void init()>
45
46Initializes a concurrency scheduler object.
47
48=cut
49
50*/
51
52    VTABLE void init() {
53        Parrot_Scheduler_attributes * const core_struct =
54            (Parrot_Scheduler_attributes *) PMC_data(SELF);
55
56        /* Set flags for custom GC mark and destroy. */
57        PObj_custom_mark_SET(SELF);
58        PObj_custom_destroy_SET(SELF);
59
60        /* Set up the core struct. */
61        core_struct->id            = 0;
62        core_struct->next_task_id  = 0;
63        core_struct->interp        = INTERP;
64
65        /* TODO: Do we need to eagerly create all these PMCs, or can we create
66           them lazily on demand? */
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->task_queue    = Parrot_pmc_new(INTERP, enum_class_PMCList);
70        core_struct->foreign_tasks = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
71        core_struct->alarms        = Parrot_pmc_new(INTERP, enum_class_PMCList);
72        core_struct->all_tasks     = Parrot_pmc_new(INTERP, enum_class_Hash);
73
74        MUTEX_INIT(core_struct->task_queue_lock);
75
76        /* Chandon TODO: Delete from int-keyed hash doesn't like me. */
77        /* VTABLE_set_integer_native(interp, core_struct->all_tasks, Hash_key_type_int); */
78
79    }
80
81/*
82
83=item C<void init_pmc(PMC *data)>
84
85Initializes a new Scheduler with a C<Hash> PMC with any or all of the keys:
86
87=over 4
88
89=item C<id>
90
91An C<Integer> representing the unique identifier for this scheduler.
92
93=back
94
95=cut
96
97*/
98
99    VTABLE void init_pmc(PMC *data) {
100        PMC              *elem;
101        Parrot_Scheduler_attributes *core_struct;
102
103        if (!VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Hash")))
104            Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_INVALID_OPERATION,
105                "Scheduler initializer must be a Hash");
106
107        SELF.init();
108
109        core_struct = PARROT_SCHEDULER(SELF);
110        elem        = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "id"));
111
112        if (!PMC_IS_NULL(elem))
113            core_struct->id = VTABLE_get_integer(INTERP, elem);
114    }
115
116
117/*
118
119=item C<void push_pmc(PMC *value)>
120
121Inserts a task into the task list.
122
123=cut
124
125*/
126
127    void push_pmc(PMC *task) {
128        Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
129
130        LOCK(core_struct->task_queue_lock);
131        VTABLE_push_pmc(INTERP, core_struct->task_queue, task);
132        UNLOCK(core_struct->task_queue_lock);
133    }
134
135
136/*
137
138=item C<void unshift_pmc(PMC *value)>
139
140Inserts a task into the head of the task list.
141
142=cut
143
144*/
145
146    void unshift_pmc(PMC *task) {
147        Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
148
149        LOCK(core_struct->task_queue_lock);
150        VTABLE_unshift_pmc(INTERP, core_struct->task_queue, task);
151        UNLOCK(core_struct->task_queue_lock);
152    }
153
154
155/*
156
157=item C<PMC *shift_pmc()>
158
159Retrieves the next task from the task list.
160
161=cut
162
163*/
164
165    VTABLE PMC *shift_pmc() {
166        Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
167        PMC * task;
168
169        LOCK(core_struct->task_queue_lock);
170        task = VTABLE_shift_pmc(INTERP, core_struct->task_queue);
171        UNLOCK(core_struct->task_queue_lock);
172
173        return task;
174    }
175
176
177/*
178
179=item C<INTVAL get_integer()>
180
181Retrieves the number of pending tasks in the scheduler's task list.
182
183=cut
184
185*/
186
187    VTABLE INTVAL get_integer() :no_wb {
188        Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
189        INTVAL elements;
190
191        LOCK(core_struct->task_queue_lock);
192        elements = VTABLE_elements(INTERP, PARROT_SCHEDULER(SELF)->task_queue);
193        UNLOCK(core_struct->task_queue_lock);
194
195        return elements;
196    }
197
198
199/*
200
201=item C<void destroy()>
202
203Frees the scheduler's underlying struct.
204
205=cut
206
207*/
208    VTABLE void destroy() :no_wb {
209        UNUSED(INTERP)
210        UNUSED(SELF)
211    }
212
213
214/*
215
216=item C<void mark()>
217
218Marks any referenced strings and PMCs as live.
219
220=cut
221
222*/
223    VTABLE void mark() :no_wb {
224        if (PARROT_SCHEDULER(SELF)) {
225            Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
226
227            Parrot_gc_mark_PMC_alive(INTERP, core_struct->handlers);
228            Parrot_gc_mark_PMC_alive(INTERP, core_struct->messages);
229            Parrot_gc_mark_PMC_alive(INTERP, core_struct->task_queue);
230            Parrot_gc_mark_PMC_alive(INTERP, core_struct->foreign_tasks);
231            Parrot_gc_mark_PMC_alive(INTERP, core_struct->alarms);
232            Parrot_gc_mark_PMC_alive(INTERP, core_struct->all_tasks);
233       }
234    }
235
236
237/*
238
239=item C<void visit(PMC *info)>
240
241Visits the contents of the scheduler (used by freeze/thaw).
242
243C<*info> is the visit info (see F<include/parrot/pmc_freeze.h>).
244
245=cut
246
247*/
248
249    VTABLE void visit(PMC *info) :no_wb {
250        /* 1) visit task list */
251        VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, task_queue);
252
253        /* 2) visit the handlers */
254        VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, handlers);
255
256        /* 3) visit the alarms */
257        VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, alarms);
258
259        /* 3) visit all tasks */
260        VISIT_PMC_ATTR(INTERP, info, SELF, Scheduler, all_tasks);
261    }
262
263
264/*
265
266=item C<void freeze(PMC *info)>
267
268Archives the scheduler.
269
270=cut
271
272*/
273
274    VTABLE void freeze(PMC *info) :no_wb {
275        Parrot_Scheduler_attributes * const core_struct = PARROT_SCHEDULER(SELF);
276
277        /* 1) freeze scheduler id */
278        VTABLE_push_integer(INTERP, info, core_struct->id);
279    }
280
281/*
282
283=item C<void thaw(PMC *info)>
284
285Unarchives the scheduler.
286
287=cut
288
289*/
290
291    VTABLE void thaw(PMC *info) {
292        /* 1. thaw scheduler id */
293        const INTVAL id = VTABLE_shift_integer(INTERP, info);
294
295        /* Allocate the scheduler's core data struct and set custom flags. */
296        SELF.init();
297
298        /* Set the scheduler's id to the frozen id */
299        PARROT_SCHEDULER(SELF)->id = id;
300    }
301
302
303/*
304
305=item C<void thawfinish(PMC *info)>
306
307Finishes thawing the scheduler.
308
309=cut
310
311*/
312
313    VTABLE void thawfinish(PMC *info) :no_wb {
314        UNUSED(info)
315        UNUSED(INTERP)
316        UNUSED(SELF)
317        /* Parrot_cx_refresh_task_list(INTERP, SELF); */
318    }
319
320
321/*
322
323=back
324
325=head2 Methods
326
327=over 4
328
329=cut
330
331*/
332
333/*
334
335=item C<METHOD active_tasks()>
336
337Returns a ResizablePMCArray containing pointers to all active tasks.
338
339=cut
340
341*/
342
343    METHOD active_tasks() {
344        Parrot_Scheduler_attributes *sdata = PARROT_SCHEDULER(SELF);
345        PMC * const tasks = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
346        PMC * const iter  = Parrot_pmc_new_init(INTERP, enum_class_HashIterator, sdata->all_tasks);
347
348        while (!VTABLE_get_bool(INTERP, iter)) {
349            PMC * const pair = VTABLE_shift_pmc(INTERP, iter);
350            PMC * task = PMCNULL;
351            STRING * const value = CONST_STRING(INTERP, "value");
352            Parrot_pcc_invoke_method_from_c_args(INTERP, pair,
353                value, "->P", &task);
354            VTABLE_push_pmc(INTERP, tasks, task);
355        }
356
357        RETURN(PMC* tasks);
358    }
359}
360
361/*
362
363=back
364
365=head1 SEE ALSO
366
367F<docs/pdds/pdd25_concurrency.pod>.
368
369=cut
370
371*/
372
373/*
374 * Local variables:
375 *   c-file-style: "parrot"
376 * End:
377 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
378 */
379