view AnimationMaster.c @ 268:e5bd470b562b

Checkpoint 2 -- split up header file, and about to delete single lang version of AnimationMaster
author Sean Halle <seanhalle@yahoo.com>
date Mon, 14 Jan 2013 15:31:23 -0800
parents 608833ae2c5d
children e6a68e7ea63f
line source
1 /*
2 * Copyright 2010 OpenSourceResearchInstitute
3 *
4 * Licensed under BSD
5 */
9 #include <stdio.h>
10 #include <stddef.h>
12 #include "PR.h"
13 #include "VSs_impl/VSs.h"
15 /*
16 void PRHandle_CreateTask_SL(SlaveVP *slave);
18 void PRHandle_CreateSlave_SL(SlaveVP *slave);
19 void PRHandle_Dissipate_SL(SlaveVP *slave);
20 void PR_int__handle_PRServiceReq_SL(SlaveVP *slave);
21 */
22 inline void PRHandle_CreateTask( PRReqst *req, SlaveVP *slave );
23 inline void PRHandle_EndTask( PRReqst *req, SlaveVP *slave );
24 inline void PRHandle_CreateSlave(PRReqst *req, SlaveVP *slave );
25 void PRHandle_Dissipate( PRReqst *req, SlaveVP *slave );
28 //inline void masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot );
29 inline void masterFunction_MultiLang( AnimSlot *slot );
30 inline PRProcess * pickAProcess( AnimSlot *slot );
31 inline SlaveVP * assignWork( PRProcess *process, AnimSlot *slot );
33 /*The animationMaster embodies most of the animator of the language. The
34 * animator is what emodies the behavior of language constructs.
35 * As such, it is the animationMaster, in combination with the plugin
36 * functions, that make the language constructs do their behavior.
37 *
38 *Within the code, this is the top-level-function of the masterVPs, and
39 * runs when the coreController has no more slave VPs. It's job is to
40 * refill the animation slots with slaves that have work.
41 *
42 *There are multiple versions of the master, each tuned to a specific
43 * combination of modes. This keeps the master simple, with reduced overhead,
44 * when the application is not using the extra complexity.
45 *
46 *As of Sept 2012, the versions available will be:
47 * 1) Single langauge, which only exposes slaves (such as SSR or Vthread)
48 * 2) Single language, which only exposes tasks (such as pure dataflow)
49 * 3) Single language, which exposes both (like Cilk, StarSs, and OpenMP)
50 * 4) Multi-language, which always assumes both tasks and slaves
51 * 5) Multi-language and multi-process, which also assumes both tasks and slaves
52 *
53 *
54 *
55 */
57 //This version of the master selects one of three loops, depending upon
58 // whether stand-alone single language (just slaves), or standalone with
59 // tasks, or multi-lang (implies multi-process)
60 void animationMaster( void *_environment, SlaveVP *masterVP )
61 {
62 TopEnv *masterEnv = (TopEnv *)_environment;
63 int32 slotIdx;
64 AnimSlot *currSlot;
65 //Used while scanning and filling animation slots
66 AnimSlot **animSlots;
68 //Local copies, for performance
69 int32 thisCoresIdx;
71 //======================== Initializations ========================
72 thisCoresIdx = masterVP->coreAnimatedBy;
73 animSlots = masterEnv->allAnimSlots[thisCoresIdx];
75 HOLISTIC__Insert_Master_Global_Vars;
77 //======================== animationMaster ========================
78 //Have three different modes, and the master behavior is different for
79 // each, so jump to the loop that corresponds to the mode.
80 //
81 switch(masterEnv->mode)
82 {
83 /*
84 { case SingleLang:
85 while(1)
86 { MEAS__Capture_Pre_Master_Point
87 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
88 {
89 currSlot = animSlots[ slotIdx ];
91 masterFunction_StandaloneSlavesOnly( masterEnv, currSlot );
92 }
93 MEAS__Capture_Post_Master_Point;
94 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
95 flushRegisters();
96 }
98 case SingleLang:
99 { PRLangEnv *protoLangEnv = _PRTopEnv->protoLangEnv;
100 while(1)
101 { MEAS__Capture_Pre_Master_Point
102 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
103 {
104 currSlot = animSlots[ slotIdx ];
106 masterFunction_SingleLang( protoLangEnv, currSlot );
107 }
108 MEAS__Capture_Post_Master_Point;
109 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
110 flushRegisters();
111 }
112 }
113 */
114 case MultiLang:
115 { while(1)
116 { MEAS__Capture_Pre_Master_Point
117 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
118 {
119 currSlot = animSlots[ slotIdx ];
121 masterFunction_MultiLang( currSlot );
122 }
123 MEAS__Capture_Post_Master_Point;
124 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
125 flushRegisters();
126 }
127 }
128 }
129 }
133 //===================== The versions of the Animation Master =================
134 //
135 //==============================================================================
137 /* 1) This version is for a single language, that has only slaves, no tasks,
138 * such as Vthread or SSR.
139 *This version is for when an application has only a single language, and
140 * that language exposes slaves explicitly (as opposed to a task based
141 * language like pure dataflow).
142 *
143 *
144 *It scans the animation slots for just-completed slaves.
145 * Each completed slave has a request in it. So, the master hands each to
146 * the plugin's request handler (there is only one plugin, because only one
147 * lang).
148 *Each request represents a language construct that has been encountered
149 * by the application code in the slave. Passing the request to the
150 * request handler is how that language construct's behavior gets invoked.
151 * The request handler then performs the actions of the construct's
152 * behavior. So, the request handler encodes the behavior of the
153 * language's parallelism constructs, and performs that when the master
154 * hands it a slave containing a request to perform that construct.
155 *
156 *On a shared-memory machine, the behavior of parallelism constructs
157 * equals control, over order of execution of code. Hence, the behavior
158 * of the language constructs performed by the request handler is to
159 * choose the order that slaves get animated, and thereby control the
160 * order that application code in the slaves executes.
161 *
162 *To control order of animation of slaves, the request handler has a
163 * language environment that holds data structures used to hold slaves
164 * and choose when they're ready to be animated.
165 *
166 *Once a slave is marked as ready to be animated by the request handler,
167 * it is the second plugin function, the Assigner, which chooses the core
168 * the slave gets assigned to for animation. Hence, the Assigner doesn't
169 * perform any of the semantic behavior of language constructs, rather
170 * it gives the language a chance to improve performance. The performance
171 * of application code is strongly related to communication between
172 * cores. On shared-memory machines, communication is caused during
173 * execution of code, by memory accesses, and how much depends on contents
174 * of caches connected to the core executing the code. So, the placement
175 * of slaves determines the communication caused during execution of the
176 * slave's code.
177 *The point of the Assigner, then, is to use application information during
178 * execution of the program, to make choices about slave placement onto
179 * cores, with the aim to put slaves close to caches containing the data
180 * used by the slave's code.
181 *
182 *==========================================================================
183 *In summary, the animationMaster scans the slots, finds slaves
184 * just-finished, which hold requests, pass those to the request handler,
185 * along with the language environment, and the request handler then manages
186 * the structures in the language env, which controls the order of
187 * animation of slaves, and so embodies the behavior of the language
188 * constructs.
189 *The animationMaster then rescans the slots, offering each empty one to
190 * the Assigner, along with the language environment. The Assigner chooses
191 * among the ready slaves in the language env, finding the one best suited
192 * to be animated by that slot's associated core.
193 *
194 *==========================================================================
195 *Implementation Details:
196 *
197 *There is a separate masterVP for each core, but a single language
198 * environment shared by all cores. Each core also has its own scheduling
199 * slots, which are used to communicate slaves between animationMaster and
200 * coreController. There is only one global variable, _PRTopEnv, which
201 * holds the language env and other things shared by the different
202 * masterVPs. The request handler and Assigner are registered with
203 * the animationMaster by the language's init function, and a pointer to
204 * each is in the _PRTopEnv. (There are also some pthread related global
205 * vars, but they're only used during init of PR).
206 *PR gains control over the cores by essentially "turning off" the OS's
207 * scheduler, using pthread pin-to-core commands.
208 *
209 *The masterVPs are created during init, with this animationMaster as their
210 * top level function. The masterVPs use the same SlaveVP data structure,
211 * even though they're not slave VPs.
212 *A "seed slave" is also created during init -- this is equivalent to the
213 * "main" function in C, and acts as the entry-point to the PR-language-
214 * based application.
215 *The masterVPs share a single system-wide master-lock, so only one
216 * masterVP may be animated at a time.
217 *The core controllers access _PRTopEnv to get the masterVP, and when
218 * they start, the slots are all empty, so they run their associated core's
219 * masterVP. The first of those to get the master lock sees the seed slave
220 * in the shared language environment, so when it runs the Assigner, that
221 * returns the seed slave, which the animationMaster puts into a scheduling
222 * slot then switches to the core controller. That then switches the core
223 * over to the seed slave, which then proceeds to execute language
224 * constructs to create more slaves, and so on. Each of those constructs
225 * causes the seed slave to suspend, switching over to the core controller,
226 * which eventually switches to the masterVP, which executes the
227 * request handler, which uses PR primitives to carry out the creation of
228 * new slave VPs, which are marked as ready for the Assigner, and so on..
229 *
230 *On animation slots, and system behavior:
231 * A request may linger in an animation slot for a long time while
232 * the slaves in the other slots are animated. This only becomes a problem
233 * when such a request is a choke-point in the constraints, and is needed
234 * to free work for *other* cores. To reduce this occurrence, the number
235 * of animation slots should be kept low. In balance, having multiple
236 * animation slots amortizes the overhead of switching to the masterVP and
237 * executing the animationMaster code, which drives for more than one. In
238 * practice, the best balance should be discovered by profiling.
239 */
240 /*
241 void masterFunction_StandaloneSlavesOnly( AnimSlot *slot )
242 {
243 SlaveVP *slave;
244 PRReqst *req;
245 PRLangEnv *langEnv = _PRTopEnv->langEnv;
248 //======================== animationMaster ========================
250 //Check if newly-done slave in slot, which will need request handled
251 if( slot->workIsDone )
252 { slot->workIsDone = FALSE;
253 slot->needsWorkAssigned = TRUE;
256 HOLISTIC__Record_AppResponder_start;
257 MEAS__startReqHdlr;
258 //process the request made by the slave (held inside slave struc)
259 slave = slot->slaveAssignedToSlot;
260 req = slave->request;
262 //Handle task create and end first -- they're special cases..
263 switch( req->reqType )
264 { case SlvCreate: PRHandle_CreateSlave( slave ); break;
265 case SlvDissipate: PRHandle_Dissipate( slave ); break;
266 case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own language env
267 case Hardware: //for future expansion
268 case IO: //for future expansion
269 case OSCall: //for future expansion
270 PR_int__throw_exception("Not implemented"); break;
271 case Language: //normal lang request
272 {
273 (*langEnv->requestHdlr)( req->langReq, slave, langEnv );
274 }
275 }
276 HOLISTIC__Record_AppResponder_end;
277 MEAS__endReqHdlr;
278 }
279 //If slot empty, hand to Assigner to fill with a slave
280 if( slot->needsWorkAssigned )
281 { //Call plugin's Assigner to give slot a new slave
282 HOLISTIC__Record_Assigner_start;
284 if( langEnv->hasWork )
285 { (*langEnv->slaveAssigner)( langEnv, slot ); //calls PR fn that inserts work into slot
286 goto ReturnAfterAssigningWork; //quit for-loop, cause found work
287 }
288 else
289 goto NoWork;
290 }
292 NoWork:
293 //No work, if reach here..
294 {
295 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
296 coreNum = slot->coreSlotIsOn;
297 returnSlv = process->idleSlv[coreNum][slotNum];
299 //things that would normally happen in resume(), but idle VPs
300 // never go there
301 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
302 Unit newU;
303 newU.vp = returnSlv->slaveNum;
304 newU.task = returnSlv->numTimesAssignedToASlot;
305 addToListOfArrays(Unit,newU,process->unitList);
307 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
308 { Dependency newD; // to this one
309 newD.from_vp = returnSlv->slaveNum;
310 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
311 newD.to_vp = returnSlv->slaveNum;
312 newD.to_task = returnSlv->numTimesAssignedToASlot;
313 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
314 }
315 #endif
316 HOLISTIC__Record_Assigner_end;
317 return;
318 }
320 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
321 {
322 HOLISTIC__Record_Assigner_end;
323 return;
324 }
325 }
326 */
329 /*This is the master when just multi-lang, but not multi-process mode is on.
330 * This version has to handle both tasks and slaves, and do extra work of
331 * looking up the language env and handlers to use, for each completed bit of
332 * work.
333 *It also has to search through the language envs to find one with work,
334 * then ask that env's assigner to return a unit of that work.
335 *
336 *The language is written to startup in the same way as if it were the only
337 * language in the app, and it operates in the same way,
338 * the only difference between single language and multi-lang is here, in the
339 * master.
340 *This invisibility to mode is why the language has to use registration calls
341 * for everything during startup -- those calls do different things depending
342 * on whether it's single-language or multi-language mode.
343 *
344 *In this version of the master, work can either be a task or a resumed slave
345 *Having two cases makes this logic complex.. can be finishing either, and
346 * then the next available work may be either.. so really have two distinct
347 * loops that are inter-twined..
348 *
349 *Some special cases:
350 * A task-end is a special case for a few reasons (below).
351 * A task-end can't block a slave (can't cause it to "logically suspend")
352 * A task available for work can only be assigned to a special slave, which
353 * has been set aside for doing tasks, one such task-slave is always
354 * assigned to each slot. So, when a task ends, a new task is assigned to
355 * that slot's task-slave right away.
356 * But if no tasks are available, then have to switch over to looking at
357 * slaves to find one ready to resume, to find work for the slot.
358 * If a task just suspends, not ends, then its task-slave is no longer
359 * available to take new tasks, so a new task-slave has to be assigned to
360 * that slot. Then the slave of the suspended task is turned into a free
361 * task-slave and request handling is done on it as if it were a slave
362 * that suspended.
363 * After request handling, do the same sequence of looking for a task to be
364 * work, and if none, look for a slave ready to resume, as work for the slot.
365 * If a slave suspends, handle its request, then look for work.. first for a
366 * task to assign, and if none, slaves ready to resume.
367 * Another special case is when task-end is done on a free task-slave.. in
368 * that case, the slave has no more work and no way to get more.. so place
369 * it into a recycle queue.
370 * If no work is found of either type, then do a special thing to prune down
371 * the extra slaves in the recycle queue, just so don't get too many..
372 *
373 *The multi-lang thing complicates matters..
374 *
375 *For request handling, it means have to first fetch the language environment
376 * of the language, and then do the request handler pointed to by that
377 * language env.
378 *For assigning, things get more complex because of competing goals.. One
379 * goal is for language specific stuff to be used during assignment, so
380 * assigner can make higher quality decisions.. but with multiple languages,
381 * which only get mixed in the application, the assigners can't be written
382 * with knowledge of each other. So, they can only make localized decisions,
383 * and so different language's assigners may interfere with each other..
384 *
385 *So, have some possibilities available:
386 *1) can have a fixed scheduler in the proto-runtime, that all the
387 * languages give their work to.. (but then lose language-specific info,
388 * there is a standard PR format for assignment info, and the langauge
389 * attaches this to the work-unit when it gives it to PR.. also have issue
390 * with HWSim, which uses a priority Q instead of FIFO, and requests can
391 * "undo" previous work put in, so request handlers need way to manipulate
392 * the work-holding Q..) (this might be fudgeable with
393 * HWSim, if the master did a lang-supplied callback each time it assigns a
394 * unit to a slot.. then HWSim can keep exactly one unit of work in PR's
395 * queue at a time.. but this is quite hack-like.. or perhaps HWSim supplies
396 * a task-end handler that kicks the next unit of work from HWSim internal
397 * priority queue, over to PR readyQ)
398 *2) can have each language have its own language env, that holds its own
399 * work, which is assigned by its own assigner.. then the master searches
400 * through all the language envs to find one with work and asks it give work..
401 * (this has downside of blinding assigners to each other.. but does work
402 * for HWSim case)
403 *3) could make PR have a different readyQ for each core, and ask the lang
404 * to put work to the core it prefers.. but the work may be moved by PR if
405 * needed, say if one core idles for too long. This is a hybrid approach,
406 * letting the language decide which core, but PR keeps the work and does it
407 * FIFO style.. (this might als be fudgeable with HWSim, in similar fashion,
408 * but it would be complicated by having to track cores separately)
409 *
410 *Choosing 2, to keep compatibility with single-lang mode.. it allows the same
411 * assigner to be used for single-lang as for multi-lang.. the overhead of
412 * the extra master search for work is part of the price of the flexibility,
413 * but should be fairly small.. takes the first env that has work available,
414 * and whatever it returns is assigned to the slot..
415 *
416 *As a hybrid, giving an option for a unified override assigner to be registered
417 * and used.. This allows something like a static analysis to detect
418 * which languages are grouped together, and then analyze the pattern of
419 * construct calls, and generate a custom assigner that uses info from all
420 * the languages in a unified way.. Don't really expect this to happen,
421 * but making it possible.
422 */
423 /*
424 inline
425 void
426 masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot )
427 { //Scan the animation slots
428 SlaveVP *slave;
429 PRReqst *req;
431 //Check if newly-done slave in slot, which will need request handled
432 if( slot->workIsDone )
433 { slot->workIsDone = FALSE;
434 slot->needsWorkAssigned = TRUE;
436 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
437 MEAS__startReqHdlr;
440 //process the request made by the slave (held inside slave struc)
441 slave = slot->slaveAssignedToSlot;
442 req = slave->request;
444 //If the requesting slave is a slot slave, and request is not
445 // task-end, then turn it into a free task slave.
446 if( slave->typeOfVP == SlotTaskSlv && req->reqType != TaskEnd )
447 PR_int__replace_with_new_slot_slv( slave );
449 //Handle task create and end first -- they're special cases..
450 switch( req->reqType )
451 { case TaskEnd:
452 { //do PR handler, which calls lang's hdlr and does recycle of
453 // free task slave if needed -- PR handler checks for free task Slv
454 PRHandle_EndTask_SL( slave ); break;
455 }
456 case TaskCreate:
457 { //Do PR's create-task handler, which calls the lang's hdlr
458 // PR handler checks for free task Slv
459 PRHandle_CreateTask_SL( slave ); break;
460 }
461 case SlvCreate: PRHandle_CreateSlave_SL( slave ); break;
462 case SlvDissipate: PRHandle_Dissipate_SL( slave ); break;
463 case Service: PR_int__handle_PRServiceReq_SL( slave ); break; //resume into PR's own language env
464 case Hardware: //for future expansion
465 case IO: //for future expansion
466 case OSCall: //for future expansion
467 PR_int__throw_exception("Not implemented", slave, NULL); break;
468 case Language: //normal lang request
469 {
470 (*protoLangEnv->requestHdlr)( req->langReq, slave, (void*)PR_int__give_lang_env(protoLangEnv ));
471 }
472 }
474 MEAS__endReqHdlr;
475 HOLISTIC__Record_AppResponder_end;
476 } //if have request to be handled
478 //If slot empty, hand to Assigner to fill with a slave
479 if( slot->needsWorkAssigned )
480 { //Call plugin's Assigner to give slot a new slave
481 HOLISTIC__Record_Assigner_start;
483 if( protoLangEnv->hasWork )
484 { (*protoLangEnv->slaveAssigner)( protoLangEnv, slot ); //calls PR fn that inserts work into slot
485 goto ReturnAfterAssigningWork; //quit for-loop, cause found work
486 }
487 else
488 goto NoWork;
489 }
491 NoWork:
492 //No work, if reach here..
493 {
494 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
495 coreNum = slot->coreSlotIsOn;
496 returnSlv = process->idleSlv[coreNum][slotNum];
498 //things that would normally happen in resume(), but idle VPs
499 // never go there
500 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
501 Unit newU;
502 newU.vp = returnSlv->slaveNum;
503 newU.task = returnSlv->numTimesAssignedToASlot;
504 addToListOfArrays(Unit,newU,process->unitList);
506 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
507 { Dependency newD; // to this one
508 newD.from_vp = returnSlv->slaveNum;
509 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
510 newD.to_vp = returnSlv->slaveNum;
511 newD.to_task = returnSlv->numTimesAssignedToASlot;
512 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
513 }
514 #endif
515 HOLISTIC__Record_Assigner_end;
516 return;
517 }
519 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
520 {
521 HOLISTIC__Record_Assigner_end;
522 return;
523 }
524 }
525 */
527 inline
528 void
529 masterFunction_MultiLang( AnimSlot *slot )
530 { //Scan the animation slots
531 int32 magicNumber;
532 SlaveVP *slave;
533 PRLangEnv *langEnv;
534 PRReqst *req;
535 RequestHandler requestHandler;
536 PRProcess *process;
538 //Check if newly-done slave in slot, which will need request handled
539 if( slot->workIsDone )
540 { slot->workIsDone = FALSE;
541 slot->needsWorkAssigned = TRUE;
543 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
544 MEAS__startReqHdlr;
547 //process the request made by the slave (held inside slave struc)
548 slave = slot->slaveAssignedToSlot;
549 req = slave->request;
551 //If the requesting slave is a slot slave, and request is not
552 // task-end, then turn it into a free task slave.
553 if( slave->typeOfVP == SlotTaskSlv && req->reqType != TaskEnd )
554 PR_int__replace_with_new_slot_slv( slave );
556 //Handle task create and end first -- they're special cases..
557 switch( req->reqType )
558 { case TaskEnd:
559 { //do PR handler, which calls lang's hdlr and does recycle of
560 // free task slave if needed -- PR handler checks for free task Slv
561 PRHandle_EndTask( req, slave ); break;
562 }
563 case TaskCreate:
564 { //Do PR's create-task handler, which calls the lang's hdlr
565 // PR handler checks for free task Slv
566 PRHandle_CreateTask( req, slave ); break;
567 }
568 case SlvCreate: PRHandle_CreateSlave( req, slave ); break;
569 case SlvDissipate: PRHandle_Dissipate( req, slave ); break;
570 case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own language env
571 case Hardware: //for future expansion
572 case IO: //for future expansion
573 case OSCall: //for future expansion
574 PR_int__throw_exception("Not implemented", slave, NULL); break;
575 case Language: //normal lang request
576 { magicNumber = req->langMagicNumber;
577 langEnv = PR_PI__give_lang_env_for( slave, magicNumber );
578 (*req->handler)( req->langReq, slave, langEnv );
579 }
580 }
582 MEAS__endReqHdlr;
583 HOLISTIC__Record_AppResponder_end;
584 } //if have request to be handled
586 if( slot->needsWorkAssigned )
587 {
588 HOLISTIC__Record_Assigner_start;
590 //Pick a process to get this slot
591 process = pickAProcess( slot );
593 //Scan lang environs, looking for langEnv with ready work.
594 // call the Assigner for that lang Env, to get a slave for the slot
595 assignWork( process, slot );
597 HOLISTIC__Record_Assigner_end;
598 }//if slot needs slave assigned
599 }
601 /*When several processes exist, use some pattern for picking one to give
602 * the animation slot to.
603 *First, it has to be a process that has work available.
604 *For now, just do a round-robin
605 */
606 inline
607 PRProcess *
608 pickAProcess( AnimSlot *slot )
609 { int32 idx;
610 PRProcess *process;
612 for( idx = _PRTopEnv->currProcessIdx; idx < _PRTopEnv->numProcesses; idx++)
613 {
614 process = _PRTopEnv->processes[ idx ];
615 if( process->numEnvsWithWork != 0 )
616 { _PRTopEnv->currProcessIdx = idx;
617 return process;
618 }
619 }
620 for( idx = 0; idx < _PRTopEnv->currProcessIdx; idx++)
621 {
622 process = _PRTopEnv->processes[ idx ];
623 if( process->numEnvsWithWork != 0 )
624 { _PRTopEnv->currProcessIdx = idx;
625 return process;
626 }
627 }
628 //none found
629 return NULL;
630 }
632 /*This does:
633 * 1) searches the language environments for one with work ready
634 * if finds one, asks its assigner to return work
635 * 2) checks what kind of work: new task, resuming task, resuming slave
636 * if new task, gets the slot slave and assigns task to it and returns slave
637 * else, gets the slave attached to the metaTask and returns that.
638 * 3) if no work found, then prune former task slaves waiting to be recycled.
639 * If no work and no slaves to prune, check for shutdown conditions.
640 *
641 * language env keeps its own work in its own structures, and has its own
642 * assigner. It chooses
643 * However, include a switch that switches-in an override assigner, which
644 * sees all the work in all the language env's. This is most likely
645 * generated by static tools and included in the executable. That means it
646 * has to be called via a registered pointer from here. The idea is that
647 * the static tools know which languages are grouped together.. and the
648 * override enables them to generate a custom assigner that uses info from
649 * all the languages in a unified way.. Don't really expect this to happen,
650 * but am making it possible.
651 */
652 inline
653 SlaveVP *
654 assignWork( PRProcess *process, AnimSlot *slot )
655 { SlaveVP *returnSlv;
656 int32 coreNum, slotNum;
657 PRMetaTask *assignedMetaTask;
659 coreNum = slot->coreSlotIsOn;
661 if( process->overrideAssigner != NULL )
662 { if( process->numEnvsWithWork != 0 )
663 { (*process->overrideAssigner)( process, slot ); //calls PR fn that inserts work into slot
664 goto ReturnAfterAssigningWork; //quit for-loop, cause found work
665 }
666 else
667 goto NoWork;
668 }
670 //If here, then no override assigner, so search language envs for work
671 int32 envIdx, numEnvs; PRLangEnv **langEnvsList, *langEnv;
672 langEnvsList = process->langEnvsList;
673 numEnvs = process->numLangEnvs;
674 for( envIdx = 0; envIdx < numEnvs; envIdx++ ) //keep langEnvs in hash & array
675 { langEnv = langEnvsList[envIdx];
676 if( langEnv->hasWork )
677 { (*langEnv->slaveAssigner)( langEnv, slot ); //assigner calls PR to put slave/task into slot
678 goto ReturnAfterAssigningWork; //quit for-loop, cause found work
679 //NOTE: bad search alg -- should start where left off, then wrap around
680 }
681 }
682 //If reach here, then have searched all langEnv's & none have work..
684 NoWork: //No work, if end up here..
685 {
686 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
687 returnSlv = process->idleSlv[coreNum][slotNum];
689 //things that would normally happen in resume(), but idle VPs
690 // never go there
691 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
692 Unit newU;
693 newU.vp = returnSlv->slaveNum;
694 newU.task = returnSlv->numTimesAssignedToASlot;
695 addToListOfArrays(Unit,newU,process->unitList);
697 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
698 { Dependency newD; // to this one
699 newD.from_vp = returnSlv->slaveNum;
700 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
701 newD.to_vp = returnSlv->slaveNum;
702 newD.to_task = returnSlv->numTimesAssignedToASlot;
703 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
704 }
705 #endif
706 HOLISTIC__Record_Assigner_end;
707 return;
708 }
710 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
711 {
712 HOLISTIC__Record_Assigner_end;
713 return;
714 }
715 }
719 /*This is first thing called when creating a slave.. it hands off to the
720 * langlet's creator, then adds updates of its own..
721 *
722 *There's a question of things like lang data, meta tasks, and such..
723 *In creator, only PR related things happen, and things for the langlet whose
724 * creator construct was used.
725 *
726 *Other langlets still get a chance to create langData -- but by registering a
727 * "createLangData" handler in the langEnv. When a construct of the langlet
728 * calls "PR__give_lang_data()", if there is no langData for that langlet,
729 * the PR will call the creator in the langlet's langEnv, place whatever it
730 * makes as the langData in that slave for that langlet, and return that langData
731 *
732 *So, as far as counting things, a langlet is only allowed to count creation
733 * of slaves it creates itself.. may have to change this later.. add a way for
734 * langlet to register a trigger Fn called each time a slave gets created..
735 * need more experience with what langlets will do at create time.. think Cilk
736 * has interesting create behavior.. not sure how that will differ in light
737 * of true tasks and langlet approach. Look at it after all done and start
738 * modifying the langs to be langlets..
739 *
740 *PR itself needs to create the slave, then update numLiveSlaves in process,
741 * copy processID from requestor to newly created
742 */
743 inline
744 void
745 PRHandle_CreateSlave( PRReqst *req, SlaveVP *slave )
746 { SlaveVP *newSlv;
747 PRProcess *process;
748 PRLangEnv *protoLangEnv;
750 process = slave->processSlaveIsIn;
751 protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave, req->langMagicNumber );
753 // newSlv = PR_int__create_slave( req->topLevelFn, req->initData );
755 //create slv has diff prototype than standard reqst hdlr
756 newSlv =
757 (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv));
759 newSlv->typeOfVP = GenericSlv;
760 newSlv->processSlaveIsIn = process;
761 newSlv->ID = req->ID;
762 process->numLiveGenericSlvs += 1;
763 }
765 /*The dissipate handler has to, update the number of slaves of the type, within
766 * the process, and call the langlet handler linked into the request,
767 * and after that returns, then call the PR function that frees the slave state
768 * (or recycles the slave).
769 *
770 *The PR function that frees the slave state has to also free all of the
771 * langData in the slave.. or else reset all of the langDatas.. by, say, marking
772 * them, then in PR__give_langData( magicNum ) call the langlet registered
773 * "resetLangData" Fn.
774 */
775 inline
776 void
777 PRHandle_Dissipate( PRReqst *req, SlaveVP *slave )
778 { PRProcess *process;
779 PRLangEnv *protoLangEnv;
781 process = slave->processSlaveIsIn;
783 //do the language's dissipate handler
784 protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave, slave->request->langMagicNumber );
786 if(req->handler != NULL)
787 (*req->handler)( req->langReq, slave, PR_int__give_lang_env(protoLangEnv) );
789 process->numLiveGenericSlvs -= 1;
790 PR_int__recycle_slave__ML( slave );
792 //check End Of Process Condition
793 if( process->numLiveTasks == 0 &&
794 process->numLiveGenericSlvs == 0 )
795 PR_SS__shutdown_process__ML( process );
796 }
798 /*Create task is a special form, that has PR behavior in addition to plugin
799 * behavior. Master calls this first, and it then calls the plugin's
800 * create task handler.
801 *
802 *Note: the requesting slave must be either generic slave or free task slave
803 */
804 inline
805 void
806 PRHandle_CreateTask( PRReqst *req, SlaveVP *slave )
807 { PRMetaTask *metaTask;
808 PRProcess *process;
809 PRLangEnv *protoLangEnv;
810 void *task;
812 process = slave->processSlaveIsIn;
814 protoLangEnv = PR_int__give_proto_lang_env_for_slave__ML( slave,
815 req->langMagicNumber );
817 //Do the langlet's create-task handler, which keeps the task
818 // inside the langlet's lang env, but returns the langMetaTask
819 // so PR can put stuff into the prolog
820 task =
821 (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv) );
822 metaTask = PR_int__give_prolog_of_task( task );
823 metaTask->ID = req->ID; //may be NULL
824 metaTask->topLevelFn = req->topLevelFn;
825 metaTask->initData = req->initData;
827 process->numLiveTasks += 1;
829 return;
830 }
832 /*When a task ends, are two scenarios: 1) task ran to completion, or 2) task
833 * suspended at some point in its code.
834 *For 1, just decr count of live tasks (and check for end condition) -- the
835 * master loop will decide what goes into the slot freed up by this task end,
836 * so, here, don't worry about assigning a new task to the slot slave.
837 *For 2, the task's slot slave has been converted to a free task slave, which
838 * now has nothing more to do, so send it to the recycle Q (which includes
839 * freeing all the langData and meta task structs alloc'd for it). Then
840 * decrement the live task count and check end condition.
841 *
842 *PR has to update count of live tasks, and check end of process condition.
843 * The "main" can invoke constructs that wait for a process to end, so when
844 * end detected, have to resume what's waiting..
845 *Thing is, that wait involves the main OS thread. That means
846 * PR internals have to do OS thread signaling. Want to do that in the
847 * core controller, which has the original stack of an OS thread. So the
848 * end process handling happens in the core controller.
849 *
850 *So here, when detect process end, signal to the core controller, which will
851 * then do the condition variable notify to the OS thread that's waiting.
852 *
853 *Note: slave may be either a slot slave or a free task slave.
854 */
855 inline
856 void
857 PRHandle_EndTask( PRReqst *req, SlaveVP *requestingSlv )
858 { void *langEnv;
859 PRProcess *process;
860 void *langMetaTask;
862 langEnv = PR_int__give_lang_env_of_req__ML( req, requestingSlv ); //magic num in req
863 langMetaTask = PR_int__give_lang_meta_task_from_slave__ML( requestingSlv, req->langMagicNumber);
865 //Do the langlet's request handler
866 //Want to keep PR structs hidden from plugin, so extract langReq..
867 (*req->handler)( req->langReq, requestingSlv, langEnv );
869 //Now that the langlet's done with it, recycle the slave if it's a freeTaskSlv
870 if( requestingSlv->typeOfVP == FreeTaskSlv )
871 PR_int__recycle_slave__ML( requestingSlv );
873 process->numLiveTasks -= 1;
875 //check End Of Process Condition
876 if( process->numLiveTasks == 0 &&
877 process->numLiveGenericSlvs == 0 )
878 { //Tell the core controller to do wakeup of any waiting OS thread
879 PR_SS__shutdown_process__ML( process );
880 }
881 }