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