view PR.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 2010 OpenSourceResearchInstitute
3 *
4 * Licensed under BSD
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <malloc.h>
11 #include <inttypes.h>
12 #include <sys/time.h>
14 #include "PR.h"
17 #define thdAttrs NULL
20 /* MEANING OF WL PI SS int
21 * These indicate which places the function is safe to use. They stand for:
22 * WL: Wrapper Library
23 * PI: Plugin
24 * SS: Startup and Shutdown
25 * int: internal to the PR implementation
26 */
29 //===========================================================================
31 //===========================================================================
33 /*Setup has two phases:
34 * 1) Semantic layer first calls init_PR, which creates masterEnv, and puts
35 * the master Slv into the work-queue, ready for first "call"
36 * 2) Semantic layer then does its own init, which creates the seed virt
37 * slave inside the semantic layer, ready to assign it when
38 * asked by the first run of the animationMaster.
39 *
40 *This part is bit weird because PR really wants to be "always there", and
41 * have applications attach and detach.. for now, this PR is part of
42 * the app, so the PR system starts up as part of running the app.
43 *
44 *The semantic layer is isolated from the PR internals by making the
45 * semantic layer do setup to a state that it's ready with its
46 * initial Slvs, ready to assign them to slots when the animationMaster
47 * asks. Without this pattern, the semantic layer's setup would
48 * have to modify slots directly to assign the initial virt-procrs, and put
49 * them into the readyToAnimateQ itself, breaking the isolation completely.
50 *
51 *
52 *The semantic layer creates the initial Slv(s), and adds its
53 * own environment to masterEnv, and fills in the pointers to
54 * the requestHandler and slaveAssigner plug-in functions
55 */
57 //Check the comments above -- likely out of sync
59 /*This allocates PR data structures, populates the top environments. After
60 * this call, processes can be started.
61 */
62 void
63 PR__start()
64 {
65 PR_SS__create_topEnv();
67 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
68 printf( "\n\n Running in SEQUENTIAL mode \n\n" );
69 //Only difference between version with an OS thread pinned to each core and
70 // the sequential version of PR is PR__init_Seq, this, and coreCtlr_Seq.
72 //Don't do anything here -- using main thread for all PR activity, so
73 // do PR activity when main thread calls "wait for process to end"
74 #else
75 DEBUG__printf1(dbgInfra,"Offset of lock in masterEnv: %d ", (int32)offsetof(TopEnv,masterLock) );
76 PR_SS__create_the_coreCtlr_OS_threads();
78 #endif
79 }
82 /*For now, this is ONLY called from the main thread -- seems this can be relaxed
83 * at some point, but want to reduce complexity to get the first version working
84 * so making this restriction for now..
85 *
86 *It creates a seed slave, from the top-level fn and initial data passed into
87 * this fn.
88 *The only langlet in the created process is the default PRServ. The rest
89 * must be started up via calls made by the seed VP's top-level fn (passed in
90 * to this call).
91 *That places the information about which langlets are used within the process
92 * into the seed Fn of that process, where all the langlet start() calls are
93 * made.
94 *
95 *A process is represented by a structure that holds all the process-specific
96 * information:
97 *-] The hash-array containing the language environs of any langlets started
98 * inside the process.
99 *-] Counter of num live slaves and num live tasks in the process
100 *
101 */
102 PRProcess *
103 PR__create_process( TopLevelFnPtr seed_Fn, void *seedData )
104 { SlaveVP *seedSlv;
105 PRProcess *process;
106 PRLangEnv **langEnvs, **langEnvsList;
108 //This runs outside of the master lock, so use PR_WL form of malloc
109 process = PR_WL__malloc( sizeof(PRProcess) );
110 _PRTopEnv->processes[_PRTopEnv->numProcesses] = process;
111 _PRTopEnv->numProcesses += 1;
113 langEnvs =
114 (PRLangEnv **)PR_int__make_collection_of_size( NUM_IN_COLLECTION );
115 langEnvsList = PR_WL__malloc( NUM_IN_COLLECTION * sizeof(PRCollElem *) );
116 process->langEnvs = langEnvs;
117 process->protoLangEnvsList = langEnvsList;
118 process->numLangEnvs = 0;
119 process->hasWaitingToEnd = FALSE;
121 //A Process starts with one slave, the seed slave
122 seedSlv = PR_int__create_slaveVP( seed_Fn, seedData, process );
123 seedSlv->typeOfVP = SeedSlv;
124 seedSlv->processSlaveIsIn = process;
125 process->numLiveGenericSlvs = 1; //count the seed
126 process->numLiveTasks = 0;
128 PRServLangEnv *
129 servicesLangEnv =
130 PRServ__start(seedSlv);
132 //resume seedVP into PR's built-in services language's lang env
133 process->numEnvsWithWork = 0; //Seed is in PRServ lang env.. resume incrs this
134 PR_PI__make_slave_ready( seedSlv, servicesLangEnv );
137 //The first process created has to unblock the core controllers.
138 // This is the "magic" that starts the activity of PR going.
139 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
140 //Do nothing here.. in sequential mode, are using the main thread, so
141 // don't use it to do anything yet.. do the PR activity when main thread
142 // calls "wait for process to end"
143 #else
144 if( _PRTopEnv->numProcesses == 1 )
145 {
146 //tell the core controller threads that a process is ready to be animated
147 //get lock, to lock out any threads still starting up -- they'll see
148 // that firstProcessReady is true before entering while loop, and so never
149 // wait on the condition
150 pthread_mutex_lock( &suspendLock );
151 _PRTopEnv->firstProcessReady = 1;
152 pthread_mutex_unlock( &suspendLock );
153 pthread_cond_broadcast( &suspendCond );
154 }
155 #endif
156 pthread_mutex_init( &(process->doneLock), NULL );
157 pthread_cond_init( &(process->doneCond), NULL );
158 process->executionIsComplete = FALSE;
160 return process;
161 }
163 void
164 PR__end_seedVP( SlaveVP *seedSlv )
165 {
166 PR_WL__send_end_slave_req( NULL, (RequestHandler)&PRServ__handleDissipateSeed, seedSlv,
167 PRServ_MAGIC_NUMBER );
168 }
170 void
171 PR__end_process_from_inside( SlaveVP *seedSlv )
172 {
173 PR_WL__send_lang_request( NULL, (RequestHandler)&PRServ__handle_end_process_from_inside,
174 seedSlv, PRServ_MAGIC_NUMBER );
175 }
179 /*When all work in the process has completed, then return from this call.
180 * The seedVP of the process may still exist, but it has no work, nor do any
181 * other VPs..
182 *The process must be shutdown via a separate call. That shutdown frees the
183 * process struct and bookkeeping structs.
184 *First checks whether the process is done, if yes, calls the clean-up fn then
185 * returns the result extracted from the PRProcess struct.
186 *If process not done yet, then performs a wait (in a loop to be sure the
187 * wakeup is not spurious, which can happen). PR registers the wait, and upon
188 * the process ending (last SlaveVP owned by it dissipates), then PR signals
189 * this to wakeup. This then calls the cleanup fn and returns the result.
190 */
191 void *
192 PR__give_results_from_process_when_ready( PRProcess *process )
193 { void *result;
194 //First get the "ACK" lock, then do normal wait for signal, then release
195 // ACK lock, to let end-process know it can free the process struct
196 pthread_mutex_lock( &(process->doneAckLock) );
198 pthread_mutex_lock( &(process->doneLock) );
199 while( process->executionIsComplete != TRUE )
200 {
201 pthread_cond_wait( &(process->doneCond),
202 &(process->doneLock) );
203 }
204 pthread_mutex_unlock( &(process->doneLock) );
205 result = process->resultToReturn;
207 //now send "ACK" signal to process_end Fn, that it may proceed
208 pthread_mutex_unlock( &(process->doneAckLock) );
210 return result;
211 //TODO: BUG? -- can process be created and end, before this acquires the
212 // first lock? Can see some rare code that creates a bunch, before getting
213 // to waiting.. leave for now.. pain to fix..
214 }
217 /*This should only be called from main. It makes the OS thread that is animating
218 * main to suspend until the process completes shutdown.
219 */
220 void
221 PR__wait_for_process_to_end( PRProcess *process )
222 {
223 process->hasWaitingToEnd = TRUE;
225 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
226 // call the one and only core ctlr (sequential version), in the main thread.
227 if( process->executionIsComplete )
228 return;
229 else
230 { coreCtlr_Seq( NULL );
231 flushRegisters(); //Not sure why here, but leaving to be safe
232 process->executionIsComplete = TRUE;
233 }
234 #else
235 //First get the "ACK" lock, then do normal wait for signal, then release
236 // ACK lock, to let end-process know it can free the process struct
237 pthread_mutex_lock( &(process->doneAckLock) );
238 pthread_mutex_lock( &(process->doneLock) );
239 while( process->executionIsComplete != TRUE )
240 {
241 pthread_cond_wait( &(process->doneCond),
242 &(process->doneLock) );
243 }
244 pthread_mutex_unlock( &(process->doneLock) );
245 //now send "ACK" signal to process_end Fn, that it may proceed
246 pthread_mutex_unlock( &(process->doneAckLock) );
248 //TODO: BUG? -- can process be created and end, before this acquires the
249 // first lock? Can see some rare code that creates a bunch, before getting
250 // to waiting.. leave for now.. pain to fix..
251 #endif
252 }
255 void
256 PR__wait_for_all_activity_to_end()
257 {
258 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
259 //In sequential mode, can't reach this call unless all activity has
260 // already completed, so just return.
261 return;
262 #else
263 pthread_mutex_lock( &(_PRTopEnv->activityDoneLock) );
264 while( !(_PRTopEnv->allActivityIsDone) )
265 {
266 pthread_cond_wait( &(_PRTopEnv->activityDoneCond),
267 &(_PRTopEnv->activityDoneLock) );
268 }
269 pthread_mutex_unlock( &(_PRTopEnv->activityDoneLock) );
270 #endif
271 }
274 /*This info is retrieved by PRServ's "give environ string" function
275 *These Fn s are meant to be called from main, or possibly the seed slave.
276 */
277 void
278 PR__set_app_info( char *info )
279 { int32 len;
280 char *copy;
281 len = strlen(info) +1;
282 copy = PR_int__malloc(len);
283 strcpy(copy, info);
284 _PRTopEnv->metaInfo->appInfo = copy;
285 }
286 void
287 PR__set_input_info( char *info )
288 { int32 len;
289 char *copy;
290 len = strlen(info) +1;
291 copy = PR_int__malloc(len);
292 strcpy(copy, info);
293 _PRTopEnv->metaInfo->inputInfo = copy;
294 }
299 //========================== SHUT DOWN ===========================
301 /*This is called from the main thread, and causes PR's OS threads to stop
302 * then cleans up any memory allocated by PR from the OS.
303 *
304 *The main thread has a separate call it can use to wait for all work to
305 * finish, so when this is called, it just shuts down, whether there's
306 * unfinished work or not.
307 *
308 *However, cores that are performing work won't see this shutdown until
309 * they finish their current work-unit.
310 */
311 void
312 PR__shutdown()
313 { int32 coreIdx;
315 PR_SS__shutdown_OS_threads();
317 //wait for the OS threads to exit
318 for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ )
319 {
320 pthread_join( coreCtlrThdHandles[coreIdx], NULL );
321 }
323 //Before getting rid of everything, print out any measurements made
324 PR_SS__print_out_measurements();
326 //free all memory allocated from the OS
327 PR_SS__cleanup_at_end_of_shutdown();
328 }
330 //====================================================