Mercurial > cgi-bin > hgwebdir.cgi > VMS > VMS_Implementations > VMS_impls > VMS__MC_shared_impl
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 }
