view AnimationMaster.c @ 287:15ee3fe10e3d

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