view VSs.c @ 26:a60399b62614

Update.. in middle of changes..
author Sean Halle <seanhalle@yahoo.com>
date Thu, 18 Oct 2012 02:44:30 -0700
parents 459055db7fc0
children 3b30da4643d1
line source
1 /*
2 * Copyright 2010 OpenSourceCodeStewardshipFoundation
3 *
4 * Licensed under BSD
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <malloc.h>
11 #include "Queue_impl/PrivateQueue.h"
12 #include "Hash_impl/PrivateHash.h"
14 #include "VSs.h"
15 #include "Measurement/VSs_Counter_Recording.h"
17 //==========================================================================
19 //==========================================================================
23 //===========================================================================
26 /*These are the library functions *called in the application*
27 *
28 *There's a pattern for the outside sequential code to interact with the
29 * PR_HW code.
30 *The PR_HW system is inside a boundary.. every VSs system is in its
31 * own directory that contains the functions for each of the processor types.
32 * One of the processor types is the "seed" processor that starts the
33 * cascade of creating all the processors that do the work.
34 *So, in the directory is a file called "EntryPoint.c" that contains the
35 * function, named appropriately to the work performed, that the outside
36 * sequential code calls. This function follows a pattern:
37 *1) it calls VSs__init()
38 *2) it creates the initial data for the seed processor, which is passed
39 * in to the function
40 *3) it creates the seed VSs processor, with the data to start it with.
41 *4) it calls startVSsThenWaitUntilWorkDone
42 *5) it gets the returnValue from the transfer struc and returns that
43 * from the function
44 *
45 *For now, a new VSs system has to be created via VSs__init every
46 * time an entry point function is called -- later, might add letting the
47 * VSs system be created once, and let all the entry points just reuse
48 * it -- want to be as simple as possible now, and see by using what makes
49 * sense for later..
50 */
54 //===========================================================================
56 int32
57 VSs__giveMinWorkUnitCycles( float32 percentOverhead )
58 {
59 return MIN_WORK_UNIT_CYCLES;
60 }
62 int32
63 VSs__giveIdealNumWorkUnits()
64 {
65 return NUM_ANIM_SLOTS * NUM_CORES;
66 }
68 int32
69 VSs__give_number_of_cores_to_schedule_onto()
70 {
71 return NUM_CORES;
72 }
74 /*For now, use TSC -- later, make these two macros with assembly that first
75 * saves jump point, and second jumps back several times to get reliable time
76 */
77 void
78 VSs__begin_primitive()
79 { VSsSemData *semData;
81 semData = (VSsSemData *)PR_WL__give_sem_data( animSlv, VSs_MAGIC_NUMBER);
83 saveLowTimeStampCountInto( semData->primitiveStartTime );
84 }
86 /*Just quick and dirty for now -- make reliable later
87 * will want this to jump back several times -- to be sure cache is warm
88 * because don't want comm time included in calc-time measurement -- and
89 * also to throw out any "weird" values due to OS interrupt or TSC rollover
90 */
91 int32
92 VSs__end_primitive_and_give_cycles( SlaveVP animSlv )
93 { int32 endTime, startTime;
94 VSsSemData *semData;
96 //TODO: fix by repeating time-measurement
97 saveLowTimeStampCountInto( endTime );
98 semData = (VSsSemData *)PR_WL__give_sem_data( animSlv, VSs_MAGIC_NUMBER);
99 startTime = semData->primitiveStartTime;
100 return (endTime - startTime);
101 }
104 //===========================================================================
109 //===========================================================================
111 SlaveVP *
112 VSs__create_thread( TopLevelFnPtr fnPtr, void *initData,
113 SlaveVP *creatingThd )
114 {
115 return VSs__create_thread_w_ID_and_affinity( fnPtr, initData, NO_ID,
116 ANY_CORE, creatingThd );
117 }
119 SlaveVP *
120 VSs__create_thread_w_ID( TopLevelFnPtr fnPtr, void *initData, int32 *thdID,
121 SlaveVP *creatingThd )
122 {
123 return VSs__create_thread_w_ID_and_affinity( fnPtr, initData, thdID,
124 ANY_CORE, creatingThd );
125 }
128 SlaveVP *
129 VSs__create_thread_w_ID_and_affinity( TopLevelFnPtr fnPtr, void *initData,
130 int32 *thdID, int32 coreToAssignOnto, SlaveVP *creatingThd )
131 { VSsSemReq reqData;
133 //the semantic request data is on the stack and disappears when this
134 // call returns -- it's guaranteed to remain in the VP's stack for as
135 // long as the VP is suspended.
136 reqData.reqType = create_slave; //know type because in a PR create req
137 reqData.coreToAssignOnto = coreToAssignOnto;
139 PR_WL__send_create_slaveVP_req( &reqData, fnPtr, initData, thdID,
140 creatingThd, VSs_MAGIC_NUMBER );
141 return creatingThd->dataRetFromReq;
142 }
144 /*This is always the last thing done in the code animated by a thread VP.
145 * Normally, this would be the last line of the thread's top level function.
146 * But, if the thread exits from any point, it has to do so by calling
147 * this.
148 *
149 *It simply sends a dissipate request, which handles all the state cleanup.
150 */
151 void
152 VSs__end_thread( SlaveVP *thdToEnd )
153 {
154 PR_WL__send_dissipate_req( thdToEnd, VSs_MAGIC_NUMBER );
155 }
159 //===========================================================================
162 //======================= task submit and end ==============================
163 /*
164 */
165 void
166 VSs__submit_task( VSsTaskType *taskType, void *args, SlaveVP *animSlv)
167 { VSsSemReq reqData;
169 reqData.reqType = submit_task;
171 reqData.taskType = taskType;
172 reqData.args = args;
173 reqData.callingSlv = animSlv;
175 //Create task is a special form, so have to pass as parameters, the
176 // top-level-fn of task and the data for that fn, plus lang's req,
177 // animating slave, and lang's magic number
178 PR_WL__send_create_task_req( taskType->fn, args, &reqData, NO_ID, animSlv, VSs_MAGIC_NUMBER );
179 }
181 void
182 VSs__submit_task_with_ID( VSsTaskType *taskType, void *args, int32 *taskID,
183 SlaveVP *animSlv)
184 { VSsSemReq reqData;
186 reqData.reqType = submit_task;
188 reqData.taskType = taskType;
189 reqData.args = args;
190 reqData.callingSlv = animSlv;
192 PR_WL__send_create_task_req( taskType->fn, args, &reqData, taskID, animSlv, VSs_MAGIC_NUMBER );
193 }
196 /*This call is the last to happen in every task. It causes the slave to
197 * suspend and get the next task out of the task-queue. Notice there is no
198 * assigner here.. only one slave, no slave ReadyQ, and so on..
199 *Can either make the assigner take the next task out of the taskQ, or can
200 * leave all as it is, and make task-end take the next task.
201 *Note: this fits the case in the new PR for no-context tasks, so will use
202 * the built-in taskQ of new PR, and should be local and much faster.
203 *
204 *The task-stub is saved in the animSlv, so the request handler will get it
205 * from there, along with the task-type which has arg types, and so on..
206 *
207 * NOTE: if want, don't need to send the animating SlaveVP around..
208 * instead, can make a single slave per core, and coreCtrlr looks up the
209 * slave from having the core number.
210 *
211 *But, to stay compatible with all the other PR languages, leave it in..
212 */
213 void
214 VSs__end_task( SlaveVP *animSlv )
215 { VSsSemReq reqData;
217 reqData.reqType = end_task;
218 reqData.callingSlv = animSlv;
220 PR_WL__send_end_task_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
221 }
224 /*Waits for all tasks that are direct children to end, then resumes calling
225 * task or thread
226 */
227 void
228 VSs__taskwait(SlaveVP *animSlv)
229 {
230 VSsSemReq reqData;
232 reqData.reqType = taskwait;
233 reqData.callingSlv = animSlv;
235 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
236 }
240 //========================== send and receive ============================
241 //
243 inline int32 *
244 VSs__give_self_taskID( SlaveVP *animSlv )
245 {
246 return PR__give_task_ID( animSlv, VSs_MAGIC_NUMBER );
247 }
249 //================================ send ===================================
251 void
252 VSs__send_of_type_to( void *msg, const int32 type, int32 *receiverID,
253 SlaveVP *senderSlv )
254 { VSsSemReq reqData;
256 reqData.reqType = send_type_to;
258 reqData.msg = msg;
259 reqData.msgType = type;
260 reqData.receiverID = receiverID;
261 reqData.senderSlv = senderSlv;
263 reqData.nextReqInHashEntry = NULL;
265 PR_WL__send_sem_request( &reqData, senderSlv, VSs_MAGIC_NUMBER );
267 //When come back from suspend, no longer own data reachable from msg
268 }
270 void
271 VSs__send_from_to( void *msg, int32 *senderID, int32 *receiverID, SlaveVP *senderSlv )
272 { VSsSemReq reqData;
274 reqData.reqType = send_from_to;
276 reqData.msg = msg;
277 reqData.senderID = senderID;
278 reqData.receiverID = receiverID;
279 reqData.senderSlv = senderSlv;
281 reqData.nextReqInHashEntry = NULL;
283 PR_WL__send_sem_request( &reqData, senderSlv, VSs_MAGIC_NUMBER );
284 }
287 //================================ receive ================================
289 /*The "type" version of send and receive creates a many-to-one relationship.
290 * The sender is anonymous, and many sends can stack up, waiting to be
291 * received. The same receiver can also have send from-to's
292 * waiting for it, and those will be kept separate from the "type"
293 * messages.
294 */
295 void *
296 VSs__receive_type_to( const int32 type, int32* receiverID, SlaveVP *receiverSlv )
297 { DEBUG__printf1(dbgRqstHdlr,"WL: receive type to %d",receiverID[1] );
298 VSsSemReq reqData;
300 reqData.reqType = receive_type_to;
302 reqData.msgType = type;
303 reqData.receiverID = receiverID;
304 reqData.receiverSlv = receiverSlv;
306 reqData.nextReqInHashEntry = NULL;
308 PR_WL__send_sem_request( &reqData, receiverSlv, VSs_MAGIC_NUMBER );
310 return receiverSlv->dataRetFromReq;
311 }
315 /*Call this at the point a receiving task wants in-coming data.
316 * Use this from-to form when know senderID -- it makes a direct channel
317 * between sender and receiver.
318 */
319 void *
320 VSs__receive_from_to( int32 *senderID, int32 *receiverID, SlaveVP *receiverSlv )
321 {
322 VSsSemReq reqData;
324 reqData.reqType = receive_from_to;
326 reqData.senderID = senderID;
327 reqData.receiverID = receiverID;
328 reqData.receiverSlv = receiverSlv;
330 reqData.nextReqInHashEntry = NULL;
331 DEBUG__printf2(dbgRqstHdlr,"WL: receive from %d to: %d", reqData.senderID[1], reqData.receiverID[1]);
333 PR_WL__send_sem_request( &reqData, receiverSlv, VSs_MAGIC_NUMBER );
335 return receiverSlv->dataRetFromReq;
336 }
341 //==========================================================================
342 //
343 /*A function singleton is a function whose body executes exactly once, on a
344 * single core, no matter how many times the fuction is called and no
345 * matter how many cores or the timing of cores calling it.
346 *
347 *A data singleton is a ticket attached to data. That ticket can be used
348 * to get the data through the function exactly once, no matter how many
349 * times the data is given to the function, and no matter the timing of
350 * trying to get the data through from different cores.
351 */
353 /*asm function declarations*/
354 void asm_save_ret_to_singleton(VSsSingleton *singletonPtrAddr);
355 void asm_write_ret_from_singleton(VSsSingleton *singletonPtrAddr);
357 /*Fn singleton uses ID as index into array of singleton structs held in the
358 * semantic environment.
359 */
360 void
361 VSs__start_fn_singleton( int32 singletonID, SlaveVP *animSlv )
362 {
363 VSsSemReq reqData;
365 //
366 reqData.reqType = singleton_fn_start;
367 reqData.singletonID = singletonID;
369 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
370 if( animSlv->dataRetFromReq ) //will be 0 or addr of label in end singleton
371 {
372 VSsSemEnv *semEnv = PR_WL__give_sem_env_for( animSlv, VSs_MAGIC_NUMBER );
373 asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID]));
374 }
375 }
377 /*Data singleton hands addr of loc holding a pointer to a singleton struct.
378 * The start_data_singleton makes the structure and puts its addr into the
379 * location.
380 */
381 void
382 VSs__start_data_singleton( VSsSingleton **singletonAddr, SlaveVP *animSlv )
383 {
384 VSsSemReq reqData;
386 if( *singletonAddr && (*singletonAddr)->hasFinished )
387 goto JmpToEndSingleton;
389 reqData.reqType = singleton_data_start;
390 reqData.singletonPtrAddr = singletonAddr;
392 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
393 if( animSlv->dataRetFromReq ) //either 0 or end singleton's return addr
394 { //Assembly code changes the return addr on the stack to the one
395 // saved into the singleton by the end-singleton-fn
396 //The return addr is at 0x4(%%ebp)
397 JmpToEndSingleton:
398 asm_write_ret_from_singleton(*singletonAddr);
399 }
400 //now, simply return
401 //will exit either from the start singleton call or the end-singleton call
402 }
404 /*Uses ID as index into array of flags. If flag already set, resumes from
405 * end-label. Else, sets flag and resumes normally.
406 *
407 *Note, this call cannot be inlined because the instr addr at the label
408 * inside is shared by all invocations of a given singleton ID.
409 */
410 void
411 VSs__end_fn_singleton( int32 singletonID, SlaveVP *animSlv )
412 {
413 VSsSemReq reqData;
415 //don't need this addr until after at least one singleton has reached
416 // this function
417 VSsSemEnv *semEnv = PR_WL__give_sem_env_for( animSlv, VSs_MAGIC_NUMBER );
418 asm_write_ret_from_singleton(&(semEnv->fnSingletons[ singletonID]));
420 reqData.reqType = singleton_fn_end;
421 reqData.singletonID = singletonID;
423 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
425 EndSingletonInstrAddr:
426 return;
427 }
429 void
430 VSs__end_data_singleton( VSsSingleton **singletonPtrAddr, SlaveVP *animSlv )
431 {
432 VSsSemReq reqData;
434 //don't need this addr until after singleton struct has reached
435 // this function for first time
436 //do assembly that saves the return addr of this fn call into the
437 // data singleton -- that data-singleton can only be given to exactly
438 // one instance in the code of this function. However, can use this
439 // function in different places for different data-singletons.
440 asm_save_ret_to_singleton(*singletonPtrAddr);
442 reqData.reqType = singleton_data_end;
443 reqData.singletonPtrAddr = singletonPtrAddr;
445 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
446 }
448 /*This executes the function in the masterVP, so it executes in isolation
449 * from any other copies -- only one copy of the function can ever execute
450 * at a time.
451 *
452 *It suspends to the master, and the request handler takes the function
453 * pointer out of the request and calls it, then resumes the VP.
454 *Only very short functions should be called this way -- for longer-running
455 * isolation, use transaction-start and transaction-end, which run the code
456 * between as work-code.
457 */
458 void
459 VSs__animate_short_fn_in_isolation( PtrToAtomicFn ptrToFnToExecInMaster,
460 void *data, SlaveVP *animSlv )
461 {
462 VSsSemReq reqData;
464 //
465 reqData.reqType = atomic;
466 reqData.fnToExecInMaster = ptrToFnToExecInMaster;
467 reqData.dataForFn = data;
469 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
470 }
473 /*This suspends to the master.
474 *First, it looks at the VP's data, to see the highest transactionID that VP
475 * already has entered. If the current ID is not larger, it throws an
476 * exception stating a bug in the code. Otherwise it puts the current ID
477 * there, and adds the ID to a linked list of IDs entered -- the list is
478 * used to check that exits are properly ordered.
479 *Next it is uses transactionID as index into an array of transaction
480 * structures.
481 *If the "VP_currently_executing" field is non-null, then put requesting VP
482 * into queue in the struct. (At some point a holder will request
483 * end-transaction, which will take this VP from the queue and resume it.)
484 *If NULL, then write requesting into the field and resume.
485 */
486 void
487 VSs__start_transaction( int32 transactionID, SlaveVP *animSlv )
488 {
489 VSsSemReq reqData;
491 //
492 reqData.callingSlv = animSlv;
493 reqData.reqType = trans_start;
494 reqData.transID = transactionID;
496 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
497 }
499 /*This suspends to the master, then uses transactionID as index into an
500 * array of transaction structures.
501 *It looks at VP_currently_executing to be sure it's same as requesting VP.
502 * If different, throws an exception, stating there's a bug in the code.
503 *Next it looks at the queue in the structure.
504 *If it's empty, it sets VP_currently_executing field to NULL and resumes.
505 *If something in, gets it, sets VP_currently_executing to that VP, then
506 * resumes both.
507 */
508 void
509 VSs__end_transaction( int32 transactionID, SlaveVP *animSlv )
510 {
511 VSsSemReq reqData;
513 //
514 reqData.callingSlv = animSlv;
515 reqData.reqType = trans_end;
516 reqData.transID = transactionID;
518 PR_WL__send_sem_request( &reqData, animSlv, VSs_MAGIC_NUMBER );
519 }
521 //======================== Internal ==================================
522 /*
523 */
525 SlaveVP *
526 VSs__create_slave_with_affinity( TopLevelFnPtr fnPtr, void *initData,
527 SlaveVP *creatingSlv, int32 coreToAssignOnto )
528 { VSsSemReq reqData;
530 //the semantic request data is on the stack and disappears when this
531 // call returns -- it's guaranteed to remain in the VP's stack for as
532 // long as the VP is suspended.
533 reqData.reqType = create_slave_w_aff; //not used, May 2012
534 reqData.coreToAssignOnto = coreToAssignOnto;
535 reqData.fnPtr = fnPtr;
536 reqData.initData = initData;
537 reqData.callingSlv = creatingSlv;
539 PR_WL__send_create_slaveVP_req( &reqData, creatingSlv, VSs_MAGIC_NUMBER );
541 return creatingSlv->dataRetFromReq;
542 }