comparison AnimationMaster.c @ 269:e6a68e7ea63f

Removed the extra level of core controller -- now only one anim slot and master called after every work unit
author Sean Halle <seanhalle@yahoo.com>
date Mon, 14 Jan 2013 16:10:37 -0800
parents e5bd470b562b
children 292393c6bef1
comparison
equal deleted inserted replaced
13:c8131981ef5d 14:098c51086089
20 void PR_int__handle_PRServiceReq_SL(SlaveVP *slave); 20 void PR_int__handle_PRServiceReq_SL(SlaveVP *slave);
21 */ 21 */
22 inline void PRHandle_CreateTask( PRReqst *req, SlaveVP *slave ); 22 inline void PRHandle_CreateTask( PRReqst *req, SlaveVP *slave );
23 inline void PRHandle_EndTask( PRReqst *req, SlaveVP *slave ); 23 inline void PRHandle_EndTask( PRReqst *req, SlaveVP *slave );
24 inline void PRHandle_CreateSlave(PRReqst *req, SlaveVP *slave ); 24 inline void PRHandle_CreateSlave(PRReqst *req, SlaveVP *slave );
25 void PRHandle_Dissipate( PRReqst *req, SlaveVP *slave ); 25 void PRHandle_EndSlave( PRReqst *req, SlaveVP *slave );
26 26
27 27
28 //inline void masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot ); 28 //inline void masterFunction_SingleLang( PRLangEnv *protoLangEnv, AnimSlot *slot );
29 inline void masterFunction_MultiLang( AnimSlot *slot ); 29 inline void masterFunction( AnimSlot *slot );
30 inline PRProcess * pickAProcess( AnimSlot *slot ); 30 inline PRProcess * pickAProcess( AnimSlot *slot );
31 inline SlaveVP * assignWork( PRProcess *process, AnimSlot *slot ); 31 inline SlaveVP * assignWork( PRProcess *process, AnimSlot *slot );
32 32
33 /*The animationMaster embodies most of the animator of the language. The 33 /*The animationMaster embodies most of the animator of the language. The
34 * animator is what emodies the behavior of language constructs. 34 * animator is what emodies the behavior of language constructs.
76 76
77 //======================== animationMaster ======================== 77 //======================== animationMaster ========================
78 //Have three different modes, and the master behavior is different for 78 //Have three different modes, and the master behavior is different for
79 // each, so jump to the loop that corresponds to the mode. 79 // each, so jump to the loop that corresponds to the mode.
80 // 80 //
81 switch(masterEnv->mode) 81 while(1)
82 { 82 { MEAS__Capture_Pre_Master_Point
83 /* 83 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++)
84 { case SingleLang: 84 {
85 while(1) 85 currSlot = animSlots[ slotIdx ];
86 { MEAS__Capture_Pre_Master_Point 86
87 for( slotIdx = 0; slotIdx < NUM_ANIM_SLOTS; slotIdx++) 87 masterFunction( currSlot );
88 { 88 }
89 currSlot = animSlots[ slotIdx ]; 89 MEAS__Capture_Post_Master_Point;
90 90 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master
91 masterFunction_StandaloneSlavesOnly( masterEnv, currSlot ); 91 flushRegisters();
92 } 92 }
93 MEAS__Capture_Post_Master_Point; 93 }
94 masterSwitchToCoreCtlr( masterVP ); //returns when ctlr switches back to master 94
95 flushRegisters();
96 }
97
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 ];
105
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 ];
120
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 }
130
131
132
133 //===================== The versions of the Animation Master =================
134 //
135 //==============================================================================
136
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;
246
247
248 //======================== animationMaster ========================
249
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;
254
255
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;
261
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;
283
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 }
291
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];
298
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);
306
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 }
319
320 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
321 {
322 HOLISTIC__Record_Assigner_end;
323 return;
324 }
325 }
326 */
327
328
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;
430
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;
435
436 HOLISTIC__Record_AppResponder_start; //TODO: update to check which process for each slot
437 MEAS__startReqHdlr;
438
439
440 //process the request made by the slave (held inside slave struc)
441 slave = slot->slaveAssignedToSlot;
442 req = slave->request;
443
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 );
448
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 }
473
474 MEAS__endReqHdlr;
475 HOLISTIC__Record_AppResponder_end;
476 } //if have request to be handled
477
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;
482
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 }
490
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];
497
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);
505
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 }
518
519 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
520 {
521 HOLISTIC__Record_Assigner_end;
522 return;
523 }
524 }
525 */
526 95
527 inline 96 inline
528 void 97 void
529 masterFunction_MultiLang( AnimSlot *slot ) 98 masterFunction( AnimSlot *slot )
530 { //Scan the animation slots 99 { //Scan the animation slots
531 int32 magicNumber; 100 int32 magicNumber;
532 SlaveVP *slave; 101 SlaveVP *slave;
533 PRLangEnv *langEnv; 102 PRLangEnv *langEnv;
534 PRReqst *req; 103 PRReqst *req;
535 RequestHandler requestHandler;
536 PRProcess *process; 104 PRProcess *process;
537 105
538 //Check if newly-done slave in slot, which will need request handled 106 //Check if newly-done slave in slot, which will need request handled
539 if( slot->workIsDone ) 107 if( slot->workIsDone )
540 { slot->workIsDone = FALSE; 108 { slot->workIsDone = FALSE;
564 { //Do PR's create-task handler, which calls the lang's hdlr 132 { //Do PR's create-task handler, which calls the lang's hdlr
565 // PR handler checks for free task Slv 133 // PR handler checks for free task Slv
566 PRHandle_CreateTask( req, slave ); break; 134 PRHandle_CreateTask( req, slave ); break;
567 } 135 }
568 case SlvCreate: PRHandle_CreateSlave( req, slave ); break; 136 case SlvCreate: PRHandle_CreateSlave( req, slave ); break;
569 case SlvDissipate: PRHandle_Dissipate( req, slave ); break; 137 case SlvDissipate: PRHandle_EndSlave( req, slave ); break;
570 case Service: PR_int__handle_PRServiceReq( slave ); break; //resume into PR's own language env 138 case Service: PR_int__handle_PRServiceReq( slave ); break; //resumes into Service lang env
571 case Hardware: //for future expansion 139 case Hardware: //for future expansion
572 case IO: //for future expansion 140 case IO: //for future expansion
573 case OSCall: //for future expansion 141 case OSCall: //for future expansion
574 PR_int__throw_exception("Not implemented", slave, NULL); break; 142 PR_int__throw_exception("Not implemented", slave, NULL); break;
575 case Language: //normal lang request 143 case Language: //normal lang request
702 newD.to_task = returnSlv->numTimesAssignedToASlot; 270 newD.to_task = returnSlv->numTimesAssignedToASlot;
703 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList); 271 addToListOfArrays(Dependency, newD ,process->ctlDependenciesList);
704 } 272 }
705 #endif 273 #endif
706 HOLISTIC__Record_Assigner_end; 274 HOLISTIC__Record_Assigner_end;
707 return; 275 return FALSE;
708 } 276 }
709 277
710 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic.. 278 ReturnAfterAssigningWork: //All paths goto here.. to provide single point for holistic..
711 { 279 {
712 HOLISTIC__Record_Assigner_end; 280 HOLISTIC__Record_Assigner_end;
713 return; 281 return TRUE;
714 } 282 }
715 } 283 }
716 284
717 285
718 286
772 * them, then in PR__give_langData( magicNum ) call the langlet registered 340 * them, then in PR__give_langData( magicNum ) call the langlet registered
773 * "resetLangData" Fn. 341 * "resetLangData" Fn.
774 */ 342 */
775 inline 343 inline
776 void 344 void
777 PRHandle_Dissipate( PRReqst *req, SlaveVP *slave ) 345 PRHandle_EndSlave( PRReqst *req, SlaveVP *slave )
778 { PRProcess *process; 346 { PRProcess *process;
779 PRLangEnv *protoLangEnv; 347 PRLangEnv *protoLangEnv;
780 348
781 process = slave->processSlaveIsIn; 349 process = slave->processSlaveIsIn;
782 350