1/*
2Copyright (C) 2001-2014, Parrot Foundation.
3
4=head1 NAME
5
6src/pmc/task.pmc - Task PMC
7
8=head1 DESCRIPTION
9
10A Task PMC represents a concurrent native thread,
11initialized at startup in a threadpool.
12
13=head2 Functions
14
15=over 4
16
17=cut
18
19*/
20
21#include "parrot/scheduler_private.h"
22#include "pmc/pmc_scheduler.h"
23#include "pmc/pmc_proxy.h"
24
25/* HEADERIZER HFILE: none */
26/* HEADERIZER BEGIN: static */
27/* HEADERIZER END: static */
28
29pmclass Task provides invokable auto_attrs {
30    ATTR UINTVAL       id;        /* Unique identifier for this task */
31    ATTR FLOATVAL      birthtime; /* The creation time stamp of the task */
32    ATTR Parrot_Interp interp;    /* The interpreter that created the task */
33    ATTR PMC          *code;      /* An (optional) code for the task */
34    ATTR PMC          *data;      /* Additional data for the task */
35    ATTR INTVAL        killed;    /* Dead tasks don't get run */
36    ATTR PMC          *mailbox;   /* List of incoming messages */
37    ATTR Parrot_mutex mailbox_lock;
38    ATTR PMC          *waiters;   /* Tasks waiting on this one */
39    ATTR Parrot_mutex waiters_lock;
40    ATTR PMC          *shared;    /* List of variables shared with this task */
41    ATTR PMC          *partner;   /* Copy of this task on the other side of a GC barrier,
42                                     meaning in another thread */
43
44/*
45
46=item C<void init()>
47
48Initialize a concurrency task object.
49
50=cut
51
52*/
53
54    VTABLE void init() {
55        Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
56        Parrot_Scheduler_attributes * const sched_data =
57                PARROT_SCHEDULER(interp->scheduler);
58
59        /* Set flags for custom GC mark. */
60        PObj_custom_mark_SET(SELF);
61
62        /* Set up the core struct. */
63        core_struct->birthtime = Parrot_floatval_time();
64        core_struct->code      = PMCNULL;
65        core_struct->data      = PMCNULL;
66        core_struct->interp    = INTERP;
67        core_struct->killed    = 0;
68        core_struct->mailbox   = PMCNULL; /* Created lazily on demand */
69        core_struct->waiters   = PMCNULL; /* Created lazily on demand */
70        core_struct->shared    = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
71        core_struct->partner   = NULL; /* Set by Parrot_thread_create_local_task */
72
73        MUTEX_INIT(core_struct->mailbox_lock);
74        MUTEX_INIT(core_struct->waiters_lock);
75
76        /* Assign a unique ID */
77        /* TODO: Fix collisions. */
78        core_struct->id = sched_data->next_task_id;
79        sched_data->next_task_id += 1;
80
81        /* By default, all flags are clear. */
82        TASK_active_CLEAR(SELF);
83        TASK_in_preempt_CLEAR(SELF);
84        TASK_recv_block_CLEAR(SELF);
85    }
86
87/*
88
89=item C<void init_pmc(PMC *data)>
90
91Initializes a new Task with a C<Hash> PMC with any or all of the keys:
92
93=over 4
94
95=item C<birthtime>
96
97The time at which this Task was created.
98
99=item C<code>
100
101An C<invokable> PMC related to this task.
102
103=item C<data>
104
105Some data that will be passed to C<code> when invoked.
106
107=back
108
109=cut
110
111*/
112
113    VTABLE void init_pmc(PMC *data) {
114        Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
115
116        SELF.init();
117
118        if (VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Sub"))) {
119            core_struct->code = data;
120        }
121        else if (VTABLE_isa(INTERP, data, CONST_STRING(INTERP, "Hash"))) {
122            PMC * elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "birthtime"));
123            if (! PMC_IS_NULL(elem))
124                core_struct->birthtime = VTABLE_get_number(INTERP, elem);
125
126            elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "code"));
127            if (! PMC_IS_NULL(elem))
128                core_struct->code = elem;
129
130            elem = VTABLE_get_pmc_keyed_str(INTERP, data, CONST_STRING(INTERP, "data"));
131            if (! PMC_IS_NULL(elem))
132                core_struct->data = elem;
133        }
134        else {
135            Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_INVALID_OPERATION,
136                "Task initializer must be a Hash or Sub");
137        }
138    }
139
140/*
141
142=item C<opcode_t *invoke(void *next)>
143
144Invokes whatever is in the Task's associated code.
145
146If the Task's data attribute is not null, pass it to the
147code as the first argument.
148
149=cut
150
151*/
152
153    VTABLE opcode_t *invoke(void *next) :no_wb {
154        Parrot_Task_attributes * const task = PARROT_TASK(SELF);
155        Parrot_Scheduler_attributes * const sdata =
156                PARROT_SCHEDULER(interp->scheduler);
157
158        PMC * const active_tasks = sdata->all_tasks;
159
160        PARROT_ASSERT(! PMC_IS_NULL(task->code));
161
162        /* If a task is pre-empted, this will be set again. */
163        TASK_in_preempt_CLEAR(SELF);
164
165        if (! task->killed) {
166            const INTVAL current_depth =
167                Parrot_pcc_get_recursion_depth(interp, CURRENT_CONTEXT(interp));
168            /* Add the task to the set of active Tasks */
169            PMC * const task_id = Parrot_pmc_new(interp, enum_class_Integer);
170            VTABLE_set_integer_native(interp, task_id, task->id);
171            VTABLE_set_pmc_keyed(interp, active_tasks, task_id, SELF);
172            TASK_active_SET(SELF);
173
174            /* Actually run the task */
175            Parrot_ext_call(interp, task->code, "P->", task->data);
176            /* Restore recursion_depth since Parrot_Sub_invoke increments recursion_depth
177               which would not be decremented anymore if the sub is preempted */
178            Parrot_pcc_set_recursion_depth(interp, CURRENT_CONTEXT(interp), current_depth);
179        }
180
181        /* Fixed possibly racy read with write in send TASK_recv_block_CLEAR(partner) */
182        if (task->killed || !TASK_in_preempt_TEST(SELF)) {
183            /* The task is done. */
184            /* Remove it from the set of active Tasks */
185
186            INTVAL i, n = 0;
187            PMC * const task_id = Parrot_pmc_new(interp, enum_class_Integer);
188            VTABLE_set_integer_native(interp, task_id, task->id);
189            TASK_active_CLEAR(SELF);
190            VTABLE_delete_keyed(interp, active_tasks, task_id);
191            task->killed = 1;
192
193            /* schedule any waiters. */
194            if (!PMC_IS_NULL(task->waiters))
195                n = VTABLE_get_integer(interp, task->waiters);
196
197            for (i = 0; i < n; ++i) {
198                PMC * const wtask = VTABLE_get_pmc_keyed_int(interp, task->waiters, i);
199                Parrot_cx_schedule_task(interp, wtask);
200            }
201
202            if (task->partner) { /* TODO how can we know if the partner's still alive? */
203                Parrot_Task_attributes * const partner_task = PARROT_TASK(task->partner);
204                LOCK(partner_task->waiters_lock);
205
206                if (!PMC_IS_NULL(partner_task->waiters)) {
207                    Parrot_block_GC_mark_locked(partner_task->interp);
208                    partner_task->killed = 1;
209
210                    n = VTABLE_get_integer(interp, partner_task->waiters);
211
212                    for (i = 0; i < n; ++i) {
213                        PMC * const wtask =
214                            VTABLE_get_pmc_keyed_int(interp, partner_task->waiters, i);
215                        Parrot_cx_schedule_immediate(partner_task->interp, wtask);
216                    }
217                    Parrot_unblock_GC_mark_locked(partner_task->interp);
218                }
219                else
220                    partner_task->killed = 1;
221
222                UNLOCK(partner_task->waiters_lock);
223            }
224        }
225
226        return (opcode_t*) next;
227    }
228
229/*
230
231=item C<PMC *clone()>
232
233Create a copy of the task, resetting status, ID, and birthtime.
234
235=cut
236
237*/
238
239    VTABLE PMC *clone() :no_wb {
240        /* Create the new task PMC, of the same type of this one (we may
241         * have been subclassed). */
242        PMC * const copy  = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
243        Parrot_Task_attributes * const new_struct = PARROT_TASK(copy);
244        const Parrot_Task_attributes * const old_struct = PARROT_TASK(SELF);
245
246        new_struct->code   = VTABLE_clone(INTERP, old_struct->code);
247        new_struct->data   = VTABLE_clone(INTERP, old_struct->data);
248        new_struct->shared = VTABLE_clone(INTERP, old_struct->shared);
249
250        return copy;
251    }
252
253/*
254
255=item C<PMC *get_attr_str(STRING *name)>
256
257Gets the value of an attribute for this task.
258
259=cut
260
261*/
262
263    VTABLE PMC *get_attr_str(STRING *name) :no_wb {
264        Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
265        PMC * value = PMCNULL;
266
267        if (Parrot_str_equal(INTERP, name, CONST_STRING(INTERP, "birthtime"))) {
268            value = Parrot_pmc_new(INTERP, enum_class_Float);
269            VTABLE_set_number_native(INTERP, value, core_struct->birthtime);
270        }
271        else if (Parrot_str_equal(INTERP, name, CONST_STRING(INTERP, "code"))) {
272            value = core_struct->code;
273        }
274        else if (Parrot_str_equal(INTERP, name, CONST_STRING(INTERP, "data"))) {
275            value = core_struct->data;
276        }
277
278        return value;
279    }
280
281/*
282
283=item C<void set_attr_str(STRING *name, PMC *value)>
284
285Sets the value of an attribute for this task.
286
287=cut
288
289*/
290
291    VTABLE void set_attr_str(STRING *name, PMC *value) {
292        Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
293
294        if (Parrot_str_equal(INTERP, name, CONST_STRING(INTERP, "birthtime"))) {
295            core_struct->birthtime = VTABLE_get_number(INTERP, value);
296        }
297        else if (Parrot_str_equal(INTERP, name, CONST_STRING(INTERP, "code"))) {
298            core_struct->code = value;
299        }
300        else if (STRING_equal(INTERP, name, CONST_STRING(INTERP, "data"))) {
301            core_struct->data = value;
302        }
303    }
304
305/*
306
307=item C<void push_pmc(PMC *value)>
308
309Add value to the list of PMCs shared with this task.
310
311=cut
312
313*/
314
315    VTABLE void push_pmc(PMC *value) :manual_wb {
316        VTABLE_push_pmc(interp, PARROT_TASK(SELF)->shared, value);
317    }
318
319/*
320
321=item C<PMC *pop_pmc()>
322
323Return a proxy for the last shared PMC.
324
325=cut
326
327*/
328
329    VTABLE PMC *pop_pmc() :manual_wb {
330        PMC *ret = VTABLE_pop_pmc(interp, PARROT_TASK(SELF)->shared);
331        RETURN(PMC *ret);
332    }
333
334/*
335
336=item C<void mark()>
337
338Mark any referenced strings and PMCs.
339
340=cut
341
342*/
343    VTABLE void mark() :no_wb {
344        Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
345        if (core_struct) {
346            Parrot_gc_mark_PMC_alive(INTERP, core_struct->code);
347            Parrot_gc_mark_PMC_alive(INTERP, core_struct->data);
348            Parrot_gc_mark_PMC_alive(INTERP, core_struct->mailbox);
349            Parrot_gc_mark_PMC_alive(INTERP, core_struct->waiters);
350            Parrot_gc_mark_PMC_alive(INTERP, core_struct->shared);
351            /* don't mark our partner, since it belongs to another GC */
352        }
353    }
354
355/*
356
357=item C<void visit(PMC *info)>
358
359This is used by freeze/thaw to visit the contents of the task.
360
361C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).
362
363=cut
364
365*/
366
367    VTABLE void visit(PMC *info) :no_wb {
368        /* 1) visit code block */
369        VISIT_PMC_ATTR(INTERP, info, SELF, Task, code);
370        VISIT_PMC_ATTR(INTERP, info, SELF, Task, data);
371        VISIT_PMC_ATTR(INTERP, info, SELF, Task, mailbox);
372        VISIT_PMC_ATTR(INTERP, info, SELF, Task, waiters);
373    }
374
375/*
376
377=item C<void freeze(PMC *info)>
378
379Used to archive the task.
380
381=cut
382
383*/
384
385    VTABLE void freeze(PMC *info) :no_wb {
386        const Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF);
387
388        VTABLE_push_float(INTERP, info, core_struct->birthtime);
389    }
390
391/*
392
393=item C<void thaw(PMC *info)>
394
395Used to unarchive the task.
396
397=cut
398
399*/
400
401    VTABLE void thaw(PMC *info) {
402        const FLOATVAL birthtime = VTABLE_shift_float(INTERP, info);
403
404        /* Allocate the task's core data struct and set custom flags. */
405        SELF.init();
406
407        /* Set the task's birthtime to the frozen birthtime */
408        PARROT_TASK(SELF)->birthtime = birthtime;
409    }
410
411/*
412
413=item C<void thawfinish(PMC *info)>
414
415Called after the task has been thawed.
416
417=cut
418
419*/
420
421    VTABLE void thawfinish(PMC *info) :no_wb {
422        UNUSED(INTERP)
423        UNUSED(SELF)
424        UNUSED(info)
425        /* Parrot_Task_attributes * const core_struct = PARROT_TASK(SELF); */
426    }
427
428/*
429
430=back
431
432=head2 Methods
433
434=over 4
435
436=item METHOD send(PMC *message)
437
438Send a message to this task.
439
440=cut
441
442*/
443
444    METHOD send(PMC *message) {
445        Parrot_Task_attributes * const tdata = PARROT_TASK(SELF);
446        LOCK(tdata->mailbox_lock);
447        if (PMC_IS_NULL(tdata->mailbox)) {
448            tdata->mailbox = Parrot_pmc_new(interp, enum_class_PMCList);
449            PARROT_GC_WRITE_BARRIER(interp, SELF);
450        }
451        VTABLE_push_pmc(interp, tdata->mailbox, message);
452        UNLOCK(tdata->mailbox_lock);
453
454        if (tdata->partner) {
455            PMC *                    const partner = tdata->partner;
456            Parrot_Task_attributes * const pdata = PARROT_TASK(partner);
457            LOCK(tdata->mailbox_lock);
458            Parrot_block_GC_mark_locked(pdata->interp);
459            if (TASK_recv_block_TEST(partner)) {
460                /* Was: racy write with read in invoke task->killed || in_preempt */
461                /* TASK_recv_block_CLEAR(partner); */
462                Parrot_cx_schedule_immediate(pdata->interp, partner);
463                TASK_recv_block_CLEAR(partner);
464            }
465            Parrot_unblock_GC_mark_locked(pdata->interp);
466            UNLOCK(tdata->mailbox_lock);
467        }
468        else {
469            if (TASK_recv_block_TEST(SELF)) {
470                TASK_recv_block_CLEAR(SELF);
471                Parrot_cx_schedule_task(interp, SELF);
472            }
473        }
474    }
475
476/*
477    METHOD receive() {
478    }
479*/
480
481/*
482
483=item METHOD code(PMC * code :optional)
484
485Read or write optional code, an C<invokable> PMC related to this task.
486
487=item METHOD data(PMC * data :optional)
488
489Reads or writes optional task-specific data, that will be passed to C<code> when invoked.
490
491=cut
492
493*/
494
495    METHOD code(PMC * code :optional, INTVAL has_code :opt_flag) :manual_wb {
496        Parrot_Task_attributes * const tdata = PARROT_TASK(SELF);
497        if (has_code) {
498            tdata->code = code;
499            PARROT_GC_WRITE_BARRIER(INTERP, SELF);
500        }
501        code = tdata->code;
502        RETURN(PMC *code);
503    }
504
505    METHOD data(PMC * data :optional, INTVAL has_data :opt_flag) :manual_wb {
506        Parrot_Task_attributes * const tdata = PARROT_TASK(SELF);
507        if (has_data) {
508            tdata->data = data;
509            PARROT_GC_WRITE_BARRIER(INTERP, SELF);
510        }
511        data = tdata->data;
512        RETURN(PMC *data);
513    }
514
515/*
516
517=item METHOD kill()
518
519Kill this task.
520
521=cut
522
523*/
524
525    METHOD kill() :no_wb {
526        Parrot_Task_attributes * const tdata = PARROT_TASK(SELF);
527        tdata->killed = 1;
528    }
529}
530
531/*
532
533=back
534
535=head1 SEE ALSO
536
537F<docs/pdds/pdd25_concurrency.pod>.
538
539=cut
540
541*/
542
543/*
544 * Local variables:
545 *   c-file-style: "parrot"
546 * End:
547 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
548 */
549