view AnimationMaster.c @ 278:2fc69e6c14ea

Dev_ML -- works in both sequential and concurrent modes
author Sean Halle <seanhalle@yahoo.com>
date Fri, 08 Mar 2013 05:34:21 -0800
parents 1d7ea1b0f176
children 72ffdb11ad8e 15ee3fe10e3d
line source
1 /*
2 * Copyright 2012 OpenSourceResearchInstitute.org
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 //========================= Local Declarations ========================
16 inline PRProcess *
17 pickAProcess( AnimSlot *slot );
18 inline bool32
19 assignWork( PRProcess *process, AnimSlot *slot );
21 inline void
22 PRHandle__CreateTask( PRReqst *req, SlaveVP *slave );
23 inline void
24 PRHandle__EndTask( PRReqst *req, SlaveVP *slave );
25 inline void
26 PRHandle__CreateSlave(PRReqst *req, SlaveVP *slave );
27 inline void
28 PRHandle__EndSlave( PRReqst *req, SlaveVP *slave );
30 inline void
31 PRHandle__LangShutdown( PRReqst *req, SlaveVP *requestingSlv );
33 inline void
34 handleMakeProbe( PRServiceReq *langReq, PRLangEnv *protoLangEnv );
35 inline void
36 handleThrowException( PRServiceReq *langReq, PRLangEnv *protoLangEnv );
38 //===========================================================================
40 /*Note: there used to be a coreController that was another animation
41 * layer below both the masterVP and the slaveVPs.. in that case, the
42 * masterVP was a virtual processor whose processor-state was the same
43 * as a slaveVP's processor sate, both implemented as a SlaveVP struct.
44 * Have removed that, and
45 * changed the masterVP implementation. Instead of being a special version
46 * of a proto-runtime virtual processor, using the slaveVP stuct, the
47 * Master "virtual processor" is now implemented as a pthread pinned to
48 * a physical core.
49 */
51 /*This is the behavior of the Master. The physical processor switches
52 * between animating the master, and animating a slave. When a slave
53 * suspends, the PR "suspend" primitive switches the physical core over
54 * to animating the masterVP, which is implemented as a pinned pthread.
55 * This function is the behavior of that masterVP.
56 *This function's job is to manage processing
57 * requests and to trigger assignment of new work to the physical core,
58 * and to manage sharing the core among processes.
59 */
60 inline
61 bool32
62 masterFunction( AnimSlot *slot )
63 { //Scan the animation slots
64 int32 magicNumber;
65 SlaveVP *slave;
66 PRLangEnv *langEnv;
67 PRReqst *req;
68 PRProcess *process;
69 bool32 didAssignWork;
71 //Check if newly-done slave in slot, which will need request handled
72 //NOTE: left over from when had a coreController & MasterVP managed
73 // several slots
74 if( slot->workIsDone )
75 { slot->workIsDone = FALSE;
76 slot->needsWorkAssigned = TRUE;
77 printf("top handle request: %d | reqType: %d\n", slot->coreSlotIsOn, (int32)req->reqType );fflush(stdin);
79 //An Idle VP has no request to handle, so skip to assign..
80 if( slot->slaveAssignedToSlot->typeOfVP != IdleVP &&
81 slot->slaveAssignedToSlot->typeOfVP != ShutdownVP)
82 {
83 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
84 MEAS__startReqHdlr;
87 //process the request made by the slave (held inside slave struc)
88 slave = slot->slaveAssignedToSlot;
89 req = slave->request;
91 //If the requesting slave is a slot slave, and request is not
92 // task-end, then turn it into a free task slave & continue
93 if( slave->typeOfVP == SlotTaskSlv && req->reqType != TaskEnd )
94 PR_int__replace_with_new_slot_slv( slave );
96 //Handle task create and end first -- they're special cases..
97 switch( req->reqType )
98 {
99 case TaskEnd:
100 { //do PR handler, which calls lang's hdlr and does recycle of
101 // free task slave if needed -- PR handler checks for free task Slv
102 PRHandle__EndTask( req, slave ); break;
103 }
104 case TaskCreate:
105 { //Do PR's create-task handler, which calls the lang's hdlr
106 // PR handler checks for free task Slv
107 PRHandle__CreateTask( req, slave ); break;
108 }
109 case SlvCreate: PRHandle__CreateSlave( req, slave ); break;
110 case SlvDissipate: PRHandle__EndSlave( req, slave ); break;
111 case Service: PRHandle__ServiceReq( slave ); break; //resumes into Service lang env
112 case Hardware: //for future expansion
113 case IO: //for future expansion
114 case OSCall: //for future expansion
115 PR_int__throw_exception("Not implemented", slave, NULL); break;
116 case LangShutdown: PRHandle__LangShutdown( req, slave ); break;
117 case Language: //normal lang request
118 { magicNumber = req->langMagicNumber;
119 langEnv = PR_PI__give_lang_env_for_slave( slave, magicNumber );
120 (*req->handler)( req->langReq, slave, langEnv );
121 }
122 }
124 MEAS__endReqHdlr;
125 HOLISTIC__Record_AppResponder_end;
126 }//if not idleVP
127 else if( slot->slaveAssignedToSlot->typeOfVP == ShutdownVP )
128 { //ShutdownVP used, essentially, as a flag, to cause terminate here
129 PR_int__release_master_lock();
130 terminateCoreCtlr( slot->slaveAssignedToSlot );
131 }
132 } //if have request to be handled
134 //NOTE: IF statement is leftover from when master managed many slots
135 didAssignWork = FALSE;
136 if( slot->needsWorkAssigned ) //can probably remove IF, now that only one slot
137 {
138 HOLISTIC__Record_Assigner_start;
140 //Pick a process to get this slot
141 process = pickAProcess( slot );
143 //Scan lang environs, looking for langEnv with ready work.
144 // call the Assigner for that lang Env, to get a slave for the slot
145 if( process != NULL )
146 { didAssignWork =
147 assignWork( process, slot );
148 }
149 HOLISTIC__Record_Assigner_end;
151 if( !didAssignWork ) //if no work assigned, be sure idle slave is in slot
152 { slot->slaveAssignedToSlot = _PRTopEnv->idleSlv[slot->coreSlotIsOn][0];
153 }
154 // fixme; //make into a loop that tries more processes if fails to assign
155 }//if slot needs slave assigned
157 return didAssignWork;
158 }
160 /*When several processes exist, use some pattern for picking one to give
161 * the animation slot to.
162 *First, it has to be a process that has work available.
163 *For now, just do a round-robin
164 */
165 inline
166 PRProcess *
167 pickAProcess( AnimSlot *slot )
168 { int32 idx;
169 PRProcess *process;
171 for( idx = _PRTopEnv->currProcessIdx; idx < _PRTopEnv->numProcesses; idx++)
172 {
173 process = _PRTopEnv->processes[ idx ];
174 if( process->numEnvsWithWork != 0 )
175 { _PRTopEnv->currProcessIdx = idx;
176 return process;
177 }
178 }
179 for( idx = 0; idx < _PRTopEnv->currProcessIdx; idx++)
180 {
181 process = _PRTopEnv->processes[ idx ];
182 if( process->numEnvsWithWork != 0 )
183 { _PRTopEnv->currProcessIdx = idx;
184 return process;
185 }
186 }
187 //none found
188 return NULL;
189 }
191 /*This does:
192 * 1) searches the language environments for one with work ready
193 * if finds one, asks its assigner to return work
194 * 2) checks what kind of work: new task, resuming task, resuming slave
195 * if new task, gets the slot slave and assigns task to it and returns slave
196 * else, gets the slave attached to the metaTask and returns that.
197 * 3) if no work found, then prune former task slaves waiting to be recycled.
198 * If no work and no slaves to prune, check for shutdown conditions.
199 *
200 * language env keeps its own work in its own structures, and has its own
201 * assigner. It chooses
202 * However, include a switch that switches-in an override assigner, which
203 * sees all the work in all the language env's. This is most likely
204 * generated by static tools and included in the executable. That means it
205 * has to be called via a registered pointer from here. The idea is that
206 * the static tools know which languages are grouped together.. and the
207 * override enables them to generate a custom assigner that uses info from
208 * all the languages in a unified way.. Don't really expect this to happen,
209 * but am making it possible.
210 */
211 inline
212 bool32
213 assignWork( PRProcess *process, AnimSlot *slot )
214 { int32 coreNum;
216 coreNum = slot->coreSlotIsOn;
218 if( process->overrideAssigner != NULL )
219 { if( process->numEnvsWithWork != 0 )
220 { (*process->overrideAssigner)( process, slot ); //calls PR fn that inserts work into slot
221 goto ReturnAfterAssigningWork; //quit for-loop, cause found work
222 }
223 else
224 goto NoWork;
225 }
227 //If here, then no override assigner, so search language envs for work
228 int32 envIdx, numEnvs; PRLangEnv **protoLangEnvsList, *protoLangEnv;
229 protoLangEnvsList = process->protoLangEnvsList;
230 numEnvs = process->numLangEnvs;
231 for( envIdx = 0; envIdx < numEnvs; envIdx++ ) //keep langEnvs in hash & array
232 { protoLangEnv = protoLangEnvsList[envIdx];
233 if( protoLangEnv->numReadyWork > 0 )
234 { bool32
235 didAssignWork =
236 (*protoLangEnv->workAssigner)( PR_int__give_lang_env(protoLangEnv), slot ); //assigner calls PR to put slave/task into slot
238 if(didAssignWork)
239 { protoLangEnv->numReadyWork -= 1;
240 if( protoLangEnv->numReadyWork == 0 )
241 { process->numEnvsWithWork -= 1;
242 }
243 goto ReturnAfterAssigningWork; //quit for-loop, 'cause found work
244 }
245 else
246 goto NoWork; //quit for-loop, cause found work
248 //NOTE: bad search alg -- should start where left off, then wrap around
249 }
250 }
251 //If reach here, then have searched all langEnv's & none have work..
253 NoWork: //No work, if end up here..
254 {
255 #ifdef HOLISTIC__TURN_ON_OBSERVE_UCC
256 returnSlv = process->idleSlv[coreNum][0]; //only one slot now, so [0]
258 //things that would normally happen in resume(), but idle VPs
259 // never go there
260 returnSlv->numTimesAssignedToASlot++; //gives each idle unit a unique ID
261 Unit newU;
262 newU.vp = returnSlv->slaveNum;
263 newU.task = returnSlv->numTimesAssignedToASlot;
264 addToListOfArrays(Unit,newU,process->unitList);
266 if (returnSlv->numTimesAssignedToASlot > 1) //make a dependency from prev idle unit
267 { Dependency newD; // to this one
268 newD.from_vp = returnSlv->slaveNum;
269 newD.from_task = returnSlv->numTimesAssignedToASlot - 1;
270 newD.to_vp = returnSlv->slaveNum;
271 newD.to_task = returnSlv->numTimesAssignedToASlot;
272 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
273 }
274 #endif
275 HOLISTIC__Record_Assigner_end;
276 return FALSE;
277 }
279 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
280 {
281 HOLISTIC__Record_Assigner_end;
282 return TRUE;
283 }
284 }
287 //=================================
288 //===
289 //=
290 /*Create task is a special form, that has PR behavior in addition to plugin
291 * behavior. Master calls this first, and it then calls the plugin's
292 * create task handler.
293 *
294 *Note: the requesting slave must be either generic slave or free task slave
295 */
296 inline
297 void
298 PRHandle__CreateTask( PRReqst *req, SlaveVP *slave )
299 { PRMetaTask *protoMetaTask;
300 PRProcess *process;
301 PRLangEnv *protoLangEnv;
302 void *task;
304 process = slave->processSlaveIsIn;
306 protoLangEnv = PR_int__give_proto_lang_env_for_slave( slave,
307 req->langMagicNumber );
309 //Do the langlet's create-task handler, which keeps the task
310 // inside the langlet's lang env, but returns the langMetaTask
311 // so that PR can then put stuff into the prolog
312 //typedef void * (*CreateHandler)( void *, SlaveVP *, void * ); //req, slv, langEnv
313 //
314 task =
315 (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv) );
316 protoMetaTask = PR_int__give_prolog_of_lang_meta_task( task );
317 protoMetaTask->ID = req->ID; //may be NULL
318 protoMetaTask->topLevelFn = req->topLevelFn;
319 protoMetaTask->initData = req->initData;
320 protoMetaTask->processTaskIsIn = process;
322 process->numLiveTasks += 1;
323 protoLangEnv->numLiveWork += 1; //used in wait statements -- small added overhead
325 return;
326 }
328 /*When a task ends, have two scenarios: 1) task ran to completion, or 2) task
329 * has been suspended at some point in its code.
330 *For 1, just decr count of live tasks (and check for end condition) -- the
331 * master loop will decide what goes into the slot freed up by this task end,
332 * so, here, don't worry about assigning a new task to the slot slave.
333 *For 2, the task's slot slave has been converted to a free task slave, which
334 * now has nothing more to do, so send it to the recycle Q (which includes
335 * freeing all the langData and meta task structs alloc'd for it). Then
336 * decrement the live task count and check end condition.
337 *
338 *PR has to update count of live tasks, and check end of process condition.
339 * The "main" can invoke constructs that wait for a process to end, so when
340 * end detected, have to resume what's waiting..
341 *Thing is, that wait involves the main OS thread. That means
342 * PR internals have to do OS thread signaling. Want to do that in the
343 * core controller, which has the original stack of an OS thread. So the
344 * end process handling happens in the core controller.
345 *
346 *So here, when detect process end, signal to the core controller, which will
347 * then do the condition variable notify to the OS thread that's waiting.
348 *
349 *Note: slave may be either a slot slave or a free task slave.
350 */
351 inline
352 void
353 PRHandle__EndTask( PRReqst *req, SlaveVP *requestingSlv )
354 { void *langEnv;
355 PRLangEnv *protoLangEnv;
356 PRProcess *process;
357 void *langMetaTask;
359 process = requestingSlv->processSlaveIsIn;
360 langEnv = PR_int__give_lang_env_of_req( req, requestingSlv ); //magic num in req
361 protoLangEnv = PR_int__give_proto_lang_env( langEnv );
363 langMetaTask = PR_int__give_lang_meta_task_from_slave( requestingSlv, req->langMagicNumber);
365 //Do the langlet's request handler
366 //Want to keep PR structs hidden from plugin, so extract langReq..
367 //This is supposed to free any langlet-malloc'd mem, including meta task
368 (*req->handler)( req->langReq, requestingSlv, langEnv );
370 protoLangEnv->numLiveWork -= 1; //used in wait statements -- small added overhead
371 if( protoLangEnv->numLiveWork == 0 &&
372 numInPrivQ( protoLangEnv->waitingForWorkToEndQ ) > 0 )
373 { SlaveVP *
374 waitingSlave = readPrivQ( protoLangEnv->waitingForWorkToEndQ );
375 //can't resume into langlet that just ended its last work!
376 // and don't have env that the waiter was created in, so resume
377 // into PRServ env..
378 void *
379 resumeEnv = PR_PI__give_lang_env_from_process( process, PRServ_MAGIC_NUMBER );
380 while( waitingSlave != NULL )
381 { //resume a slave that was waiting for work in this env to finish
382 PR_PI__make_slave_ready( waitingSlave, resumeEnv );
383 //get next waiting slave, repeat..
384 waitingSlave = readPrivQ( protoLangEnv->waitingForWorkToEndQ );
385 }
386 }
389 //Now that the langlet's done with it, recycle the slave if it's a freeTaskSlv
390 if( requestingSlv->typeOfVP == FreeTaskSlv )
391 PR_int__recycle_slaveVP( requestingSlv ); //Doesn't decr num live slaves
393 process->numLiveTasks -= 1;
394 //NOTE: end-task is unrelated to work available (just in case wondering)
396 //check End Of Process Condition
397 if( process->numLiveTasks == 0 &&
398 process->numLiveGenericSlvs == 0 )
399 { //Tell the core controller to do wakeup of any waiting OS thread
400 PR_SS__end_process_normally( process );
401 }
402 }
406 /*This is first thing called when creating a slave.. it hands off to the
407 * langlet's creator, then adds updates of its own..
408 *
409 *There's a question of things like lang data, meta tasks, and such..
410 *In creator, only PR related things happen, and things for the langlet whose
411 * creator construct was used.
412 *
413 *Other langlets still get a chance to create langData -- but by registering a
414 * "createLangData" handler in the langEnv. When a construct of the langlet
415 * calls "PR__give_lang_data()", if there is no langData for that langlet,
416 * the PR will call the creator in the langlet's langEnv, place whatever it
417 * makes as the langData in that slave for that langlet, and return that langData
418 *
419 *So, as far as counting things, a langlet is only allowed to count creation
420 * of slaves it creates itself.. may have to change this later.. add a way for
421 * langlet to register a trigger Fn called each time a slave gets created..
422 * need more experience with what langlets will do at create time.. think Cilk
423 * has interesting create behavior.. not sure how that will differ in light
424 * of true tasks and langlet approach. Look at it after all done and start
425 * modifying the langs to be langlets..
426 *
427 *PR itself needs to create the slave, then update numLiveSlaves in process,
428 * copy processID from requestor to newly created
429 */
430 inline
431 void
432 PRHandle__CreateSlave( PRReqst *req, SlaveVP *slave )
433 { SlaveVP *newSlv;
434 PRProcess *process;
435 PRLangEnv *protoLangEnv;
437 process = slave->processSlaveIsIn;
438 protoLangEnv = PR_int__give_proto_lang_env_for_slave( slave, req->langMagicNumber );
440 //create handler, or a future request handler will call PR_PI__make_slave_ready
441 // which will in turn handle updating which langlets and which processes have
442 // work available.
443 //NOTE: create slv has diff prototype than standard reqst hdlr
444 newSlv =
445 (*req->createHdlr)(req->langReq, slave, PR_int__give_lang_env(protoLangEnv));
447 newSlv->typeOfVP = GenericSlv;
448 newSlv->processSlaveIsIn = process;
449 newSlv->ID = req->ID;
450 process->numLiveGenericSlvs += 1; //not same as work ready!
451 protoLangEnv->numLiveWork += 1; //used in wait statements -- small added overhead
452 }
454 /*The dissipate handler has to, update the number of slaves of the type, within
455 * the process, and call the langlet handler linked into the request,
456 * and after that returns, then call the PR function that frees the slave state
457 * (or recycles the slave).
458 *
459 *The PR function that frees the slave state has to also free all of the
460 * langData in the slave.. or else reset all of the langDatas.. by, say, marking
461 * them, then in PR__give_langData( magicNum ) call the langlet registered
462 * "resetLangData" Fn.
463 */
464 inline
465 void
466 PRHandle__EndSlave( PRReqst *req, SlaveVP *slave )
467 { PRProcess *process;
468 PRLangEnv *protoLangEnv;
470 process = slave->processSlaveIsIn;
472 //do the language's dissipate handler
473 protoLangEnv = PR_int__give_proto_lang_env_for_slave( slave, slave->request->langMagicNumber );
475 if(req->handler != NULL)
476 (*req->handler)( req->langReq, slave, PR_int__give_lang_env(protoLangEnv) );
478 protoLangEnv->numLiveWork -= 1; //used in wait statements -- small added overhead
479 if( protoLangEnv->numLiveWork == 0 &&
480 numInPrivQ( protoLangEnv->waitingForWorkToEndQ ) > 0 )
481 { SlaveVP *
482 waitingSlave = readPrivQ( protoLangEnv->waitingForWorkToEndQ );
483 //can't resume into langlet that just ended its last work!
484 // and don't have env that the waiter was created in, so resume
485 // into PRServ env..
486 void *
487 resumeEnv = PR_PI__give_lang_env_from_process( process, PRServ_MAGIC_NUMBER );
488 while( waitingSlave != NULL )
489 { //resume a slave that was waiting for work in this env to finish
490 PR_PI__make_slave_ready( waitingSlave, resumeEnv );
491 //get next waiting slave, repeat..
492 waitingSlave = readPrivQ( protoLangEnv->waitingForWorkToEndQ );
493 }
494 }
496 process->numLiveGenericSlvs -= 1;
497 PR_int__recycle_slaveVP( slave );
498 //NOTE: dissipate is unrelated to work available (just in case wondering)
500 //check End Of Process Condition
501 if( process->numLiveTasks == 0 &&
502 process->numLiveGenericSlvs == 0 )
503 PR_SS__end_process_normally( process );
504 }
506 //=======================
507 //===
508 //=
509 /*Langlet shutdown triggers this, which calls the registered shutdown
510 * handler for the langlet, and removes the lang's env from the process
511 */
512 inline
513 void
514 PRHandle__LangShutdown( PRReqst *req, SlaveVP *requestingSlv )
515 { void *langEnv;
516 PRLangEnv *protoLangEnv;
517 PRProcess *process;
519 process = requestingSlv->processSlaveIsIn;
520 protoLangEnv = PR_int__give_proto_lang_env_from_process( process, req->langMagicNumber );
521 langEnv = PR_int__give_lang_env( protoLangEnv );
523 //call the langlet's registered handler
524 (*protoLangEnv->shutdownHdlr)( langEnv );
526 PR_int__remove_lang_env_from_process_and_free( langEnv ); //removes from process and frees
528 PR_PI__resume_slave_in_PRServ( requestingSlv );
529 }
532 /*This is for OS requests and PR infrastructure requests, which are not
533 * part of the PRServ language -- this is for things that have to be in the
534 * infrastructure of PR itself, such as I/O requests, which have to go through
535 * pthreads inside the core controller..
536 *
537 *As of Jan 2013, doesn't do much of anything..
538 */
539 void inline
540 PRHandle__ServiceReq( SlaveVP *requestingSlv )
541 { PRReqst *req;
542 PRServiceReq *langReq;
543 PRLangEnv *protoLangEnv;
544 int32 magicNumber;
547 req = requestingSlv->request;
549 magicNumber = req->langMagicNumber;
550 protoLangEnv = PR_int__give_proto_lang_env_for_slave( requestingSlv, magicNumber );
552 langReq = PR_PI__take_lang_reqst_from(req);
553 if( langReq == NULL ) return;
554 switch( langReq->reqType ) //lang handlers are all in other file
555 {
556 case make_probe: handleMakeProbe( langReq, protoLangEnv );
557 break;
558 case throw_excp: handleThrowException( langReq, protoLangEnv );
559 break;
560 }
561 }
564 /*These handlers are special -- they don't belong to a language, because they
565 * deal with things internal to PR, so put them here..
566 */
567 inline
568 void
569 handleMakeProbe( PRServiceReq *langReq, PRLangEnv *protoLangEnv )
570 { IntervalProbe *newProbe;
572 newProbe = PR_int__malloc( sizeof(IntervalProbe) );
573 newProbe->nameStr = PR_int__strDup( langReq->nameStr );
574 newProbe->hist = NULL;
575 newProbe->schedChoiceWasRecorded = FALSE;
577 //This runs in masterVP, so no race-condition worries
578 //BUG: move to process
579 newProbe->probeID =
580 addToDynArray( newProbe, _PRTopEnv->dynIntervalProbesInfo );
582 langReq->requestingSlv->dataRetFromReq = newProbe;
584 (*protoLangEnv->makeSlaveReadyFn)( langReq->requestingSlv, PR_int__give_lang_env(protoLangEnv) );
585 }
587 inline
588 void
589 handleThrowException( PRServiceReq *langReq, PRLangEnv *protoLangEnv )
590 {
591 PR_int__throw_exception( langReq->msgStr, langReq->requestingSlv, langReq->exceptionData );
593 (*protoLangEnv->makeSlaveReadyFn)( langReq->requestingSlv, PR_int__give_lang_env(protoLangEnv) );
594 }