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