annotate PR.c @ 273:40e7625e57bd

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