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