view PR.c @ 276:1d7ea1b0f176

Dev_ML Working in sequential mode
author Sean Halle <seanhalle@yahoo.com>
date Mon, 04 Mar 2013 00:40:38 -0800
parents 40e7625e57bd
children 2fc69e6c14ea
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 PR__end_seedVP( SlaveVP *seedSlv )
164 {
165 PR_WL__send_end_slave_req( NULL, (RequestHandler)&PRServ__handleDissipateSeed, seedSlv,
166 PRServ_MAGIC_NUMBER );
167 }
169 PR__end_process_from_inside( SlaveVP *seedSlv )
170 {
171 PR_WL__send_lang_request( NULL, (RequestHandler)&PRServ__handle_end_process_from_inside,
172 seedSlv, PRServ_MAGIC_NUMBER );
173 }
177 /*When all work in the process has completed, then return from this call.
178 * The seedVP of the process may still exist, but it has no work, nor do any
179 * other VPs..
180 *The process must be shutdown via a separate call. That shutdown frees the
181 * process struct and bookkeeping structs.
182 *First checks whether the process is done, if yes, calls the clean-up fn then
183 * returns the result extracted from the PRProcess struct.
184 *If process not done yet, then performs a wait (in a loop to be sure the
185 * wakeup is not spurious, which can happen). PR registers the wait, and upon
186 * the process ending (last SlaveVP owned by it dissipates), then PR signals
187 * this to wakeup. This then calls the cleanup fn and returns the result.
188 */
189 void *
190 PR__give_results_from_process_when_ready( PRProcess *process )
191 { void *result;
192 //First get the "ACK" lock, then do normal wait for signal, then release
193 // ACK lock, to let end-process know it can free the process struct
194 pthread_mutex_lock( &(process->doneAckLock) );
196 pthread_mutex_lock( &(process->doneLock) );
197 while( process->executionIsComplete != TRUE )
198 {
199 pthread_cond_wait( &(process->doneCond),
200 &(process->doneLock) );
201 }
202 pthread_mutex_unlock( &(process->doneLock) );
203 result = process->resultToReturn;
205 //now send "ACK" signal to process_end Fn, that it may proceed
206 pthread_mutex_unlock( &(process->doneAckLock) );
208 return result;
209 //TODO: BUG? -- can process be created and end, before this acquires the
210 // first lock? Can see some rare code that creates a bunch, before getting
211 // to waiting.. leave for now.. pain to fix..
212 }
215 /*This should only be called from main. It makes the OS thread that is animating
216 * main to suspend until the process completes shutdown.
217 */
218 void
219 PR__wait_for_process_to_end( PRProcess *process )
220 {
221 process->hasWaitingToEnd = TRUE;
223 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
224 // call the one and only core ctlr (sequential version), in the main thread.
225 if( process->executionIsComplete )
226 return;
227 else
228 { coreCtlr_Seq( NULL );
229 flushRegisters(); //Not sure why here, but leaving to be safe
230 process->executionIsComplete = TRUE;
231 }
232 #else
233 //First get the "ACK" lock, then do normal wait for signal, then release
234 // ACK lock, to let end-process know it can free the process struct
235 pthread_mutex_lock( &(process->doneAckLock) );
236 pthread_mutex_lock( &(process->doneLock) );
237 while( process->executionIsComplete != TRUE )
238 {
239 pthread_cond_wait( &(process->doneCond),
240 &(process->doneLock) );
241 }
242 pthread_mutex_unlock( &(process->doneLock) );
243 //now send "ACK" signal to process_end Fn, that it may proceed
244 pthread_mutex_unlock( &(process->doneAckLock) );
246 //TODO: BUG? -- can process be created and end, before this acquires the
247 // first lock? Can see some rare code that creates a bunch, before getting
248 // to waiting.. leave for now.. pain to fix..
249 #endif
250 }
253 void
254 PR__wait_for_all_activity_to_end()
255 {
256 #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE
257 //In sequential mode, can't reach this call unless all activity has
258 // already completed, so just return.
259 return;
260 #else
261 pthread_mutex_lock( &(_PRTopEnv->activityDoneLock) );
262 while( !(_PRTopEnv->allActivityIsDone) )
263 {
264 pthread_cond_wait( &(_PRTopEnv->activityDoneCond),
265 &(_PRTopEnv->activityDoneLock) );
266 }
267 pthread_mutex_unlock( &(_PRTopEnv->activityDoneLock) );
268 #endif
269 }
272 /*This info is retrieved by PRServ's "give environ string" function
273 *These Fn s are meant to be called from main, or possibly the seed slave.
274 */
275 void
276 PR__set_app_info( char *info )
277 { int32 len;
278 char *copy;
279 len = strlen(info) +1;
280 copy = PR_int__malloc(len);
281 strcpy(copy, info);
282 _PRTopEnv->metaInfo->appInfo = copy;
283 }
284 void
285 PR__set_input_info( char *info )
286 { int32 len;
287 char *copy;
288 len = strlen(info) +1;
289 copy = PR_int__malloc(len);
290 strcpy(copy, info);
291 _PRTopEnv->metaInfo->inputInfo = copy;
292 }
297 //========================== SHUT DOWN ===========================
299 /*This is called from the main thread, and causes PR's OS threads to stop
300 * then cleans up any memory allocated by PR from the OS.
301 *
302 *The main thread has a separate call it can use to wait for all work to
303 * finish, so when this is called, it just shuts down, whether there's
304 * unfinished work or not.
305 *
306 *However, cores that are performing work won't see this shutdown until
307 * they finish their current work-unit.
308 */
309 void
310 PR__shutdown()
311 { int32 coreIdx;
313 PR_SS__shutdown_OS_threads();
315 //wait for the OS threads to exit
316 for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ )
317 {
318 pthread_join( coreCtlrThdHandles[coreIdx], NULL );
319 }
321 //Before getting rid of everything, print out any measurements made
322 PR_SS__print_out_measurements();
324 //free all memory allocated from the OS
325 PR_SS__cleanup_at_end_of_shutdown();
326 }