seanhalle@273: /* seanhalle@273: * Copyright 2010 OpenSourceResearchInstitute seanhalle@273: * seanhalle@273: * Licensed under BSD seanhalle@273: */ seanhalle@273: seanhalle@273: #include seanhalle@273: #include seanhalle@273: #include seanhalle@273: #include seanhalle@273: #include seanhalle@273: #include seanhalle@273: seanhalle@273: #include "PR.h" seanhalle@273: seanhalle@273: seanhalle@273: #define thdAttrs NULL seanhalle@273: seanhalle@273: seanhalle@273: /* MEANING OF WL PI SS int seanhalle@273: * These indicate which places the function is safe to use. They stand for: seanhalle@273: * WL: Wrapper Library seanhalle@273: * PI: Plugin seanhalle@273: * SS: Startup and Shutdown seanhalle@273: * int: internal to the PR implementation seanhalle@273: */ seanhalle@273: seanhalle@273: seanhalle@273: //=========================================================================== seanhalle@273: seanhalle@273: //=========================================================================== seanhalle@273: seanhalle@273: /*Setup has two phases: seanhalle@273: * 1) Semantic layer first calls init_PR, which creates masterEnv, and puts seanhalle@273: * the master Slv into the work-queue, ready for first "call" seanhalle@273: * 2) Semantic layer then does its own init, which creates the seed virt seanhalle@273: * slave inside the semantic layer, ready to assign it when seanhalle@273: * asked by the first run of the animationMaster. seanhalle@273: * seanhalle@273: *This part is bit weird because PR really wants to be "always there", and seanhalle@273: * have applications attach and detach.. for now, this PR is part of seanhalle@273: * the app, so the PR system starts up as part of running the app. seanhalle@273: * seanhalle@273: *The semantic layer is isolated from the PR internals by making the seanhalle@273: * semantic layer do setup to a state that it's ready with its seanhalle@273: * initial Slvs, ready to assign them to slots when the animationMaster seanhalle@273: * asks. Without this pattern, the semantic layer's setup would seanhalle@273: * have to modify slots directly to assign the initial virt-procrs, and put seanhalle@273: * them into the readyToAnimateQ itself, breaking the isolation completely. seanhalle@273: * seanhalle@273: * seanhalle@273: *The semantic layer creates the initial Slv(s), and adds its seanhalle@273: * own environment to masterEnv, and fills in the pointers to seanhalle@273: * the requestHandler and slaveAssigner plug-in functions seanhalle@273: */ seanhalle@273: seanhalle@273: //Check the comments above -- likely out of sync seanhalle@273: seanhalle@273: /*This allocates PR data structures, populates the top environments. After seanhalle@273: * this call, processes can be started. seanhalle@273: */ seanhalle@273: void seanhalle@273: PR__start() seanhalle@273: { seanhalle@273: PR_SS__create_topEnv(); seanhalle@273: seanhalle@273: #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE seanhalle@273: printf( "\n\n Running in SEQUENTIAL mode \n\n" ); seanhalle@273: //Only difference between version with an OS thread pinned to each core and seanhalle@273: // the sequential version of PR is PR__init_Seq, this, and coreCtlr_Seq. seanhalle@273: seanhalle@273: //Don't do anything here -- using main thread for all PR activity, so seanhalle@273: // do PR activity when main thread calls "wait for process to end" seanhalle@273: #else seanhalle@273: DEBUG__printf1(dbgInfra,"Offset of lock in masterEnv: %d ", (int32)offsetof(TopEnv,masterLock) ); seanhalle@273: PR_SS__create_the_coreCtlr_OS_threads(); seanhalle@273: seanhalle@273: #endif seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: /*For now, this is ONLY called from the main thread -- seems this can be relaxed seanhalle@273: * at some point, but want to reduce complexity to get the first version working seanhalle@273: * so making this restriction for now.. seanhalle@273: * seanhalle@273: *It creates a seed slave, from the top-level fn and initial data passed into seanhalle@273: * this fn. seanhalle@273: *The only langlet in the created process is the default PRServ. The rest seanhalle@273: * must be started up via calls made by the seed VP's top-level fn (passed in seanhalle@273: * to this call). seanhalle@273: *That places the information about which langlets are used within the process seanhalle@273: * into the seed Fn of that process, where all the langlet start() calls are seanhalle@273: * made. seanhalle@273: * seanhalle@273: *A process is represented by a structure that holds all the process-specific seanhalle@273: * information: seanhalle@273: *-] The hash-array containing the language environs of any langlets started seanhalle@273: * inside the process. seanhalle@273: *-] Counter of num live slaves and num live tasks in the process seanhalle@273: * seanhalle@273: */ seanhalle@273: PRProcess * seanhalle@273: PR__create_process( TopLevelFnPtr seed_Fn, void *seedData ) seanhalle@273: { SlaveVP *seedSlv; seanhalle@273: PRProcess *process; seanhalle@273: PRLangEnv **langEnvs, **langEnvsList; seanhalle@273: seanhalle@273: //This runs outside of the master lock, so use PR_WL form of malloc seanhalle@273: process = PR_WL__malloc( sizeof(PRProcess) ); seanhalle@273: _PRTopEnv->processes[_PRTopEnv->numProcesses] = process; seanhalle@273: _PRTopEnv->numProcesses += 1; seanhalle@273: seanhalle@273: langEnvs = seanhalle@273: (PRLangEnv **)PR_int__make_collection_of_size( NUM_IN_COLLECTION ); seanhalle@273: langEnvsList = PR_WL__malloc( NUM_IN_COLLECTION * sizeof(PRCollElem *) ); seanhalle@273: process->langEnvs = langEnvs; seanhalle@273: process->protoLangEnvsList = langEnvsList; seanhalle@273: process->numLangEnvs = 0; seanhalle@276: process->hasWaitingToEnd = FALSE; seanhalle@276: seanhalle@273: //A Process starts with one slave, the seed slave seanhalle@273: seedSlv = PR_int__create_slaveVP( seed_Fn, seedData, process ); seanhalle@273: seedSlv->typeOfVP = SeedSlv; seanhalle@273: seedSlv->processSlaveIsIn = process; seanhalle@273: process->numLiveGenericSlvs = 1; //count the seed seanhalle@273: process->numLiveTasks = 0; seanhalle@273: seanhalle@273: PRServLangEnv * seanhalle@273: servicesLangEnv = seanhalle@273: PRServ__start(seedSlv); seanhalle@273: seanhalle@273: //resume seedVP into PR's built-in services language's lang env seanhalle@273: process->numEnvsWithWork = 0; //Seed is in PRServ lang env.. resume incrs this seanhalle@273: PR_PI__make_slave_ready( seedSlv, servicesLangEnv ); seanhalle@273: seanhalle@273: seanhalle@273: //The first process created has to unblock the core controllers. seanhalle@273: // This is the "magic" that starts the activity of PR going. seanhalle@273: #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE seanhalle@273: //Do nothing here.. in sequential mode, are using the main thread, so seanhalle@273: // don't use it to do anything yet.. do the PR activity when main thread seanhalle@273: // calls "wait for process to end" seanhalle@273: #else seanhalle@273: if( _PRTopEnv->numProcesses == 1 ) seanhalle@273: { seanhalle@273: //tell the core controller threads that a process is ready to be animated seanhalle@273: //get lock, to lock out any threads still starting up -- they'll see seanhalle@273: // that firstProcessReady is true before entering while loop, and so never seanhalle@273: // wait on the condition seanhalle@273: pthread_mutex_lock( &suspendLock ); seanhalle@273: _PRTopEnv->firstProcessReady = 1; seanhalle@273: pthread_mutex_unlock( &suspendLock ); seanhalle@273: pthread_cond_broadcast( &suspendCond ); seanhalle@273: } seanhalle@273: #endif seanhalle@273: pthread_mutex_init( &(process->doneLock), NULL ); seanhalle@273: pthread_cond_init( &(process->doneCond), NULL ); seanhalle@273: process->executionIsComplete = FALSE; seanhalle@273: seanhalle@273: return process; seanhalle@273: } seanhalle@273: seanhalle@278: void seanhalle@273: PR__end_seedVP( SlaveVP *seedSlv ) seanhalle@273: { seanhalle@273: PR_WL__send_end_slave_req( NULL, (RequestHandler)&PRServ__handleDissipateSeed, seedSlv, seanhalle@273: PRServ_MAGIC_NUMBER ); seanhalle@273: } seanhalle@273: seanhalle@278: void seanhalle@273: PR__end_process_from_inside( SlaveVP *seedSlv ) seanhalle@273: { seanhalle@273: PR_WL__send_lang_request( NULL, (RequestHandler)&PRServ__handle_end_process_from_inside, seanhalle@273: seedSlv, PRServ_MAGIC_NUMBER ); seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: seanhalle@273: /*When all work in the process has completed, then return from this call. seanhalle@273: * The seedVP of the process may still exist, but it has no work, nor do any seanhalle@273: * other VPs.. seanhalle@273: *The process must be shutdown via a separate call. That shutdown frees the seanhalle@273: * process struct and bookkeeping structs. seanhalle@273: *First checks whether the process is done, if yes, calls the clean-up fn then seanhalle@273: * returns the result extracted from the PRProcess struct. seanhalle@273: *If process not done yet, then performs a wait (in a loop to be sure the seanhalle@273: * wakeup is not spurious, which can happen). PR registers the wait, and upon seanhalle@273: * the process ending (last SlaveVP owned by it dissipates), then PR signals seanhalle@273: * this to wakeup. This then calls the cleanup fn and returns the result. seanhalle@273: */ seanhalle@273: void * seanhalle@273: PR__give_results_from_process_when_ready( PRProcess *process ) seanhalle@273: { void *result; seanhalle@273: //First get the "ACK" lock, then do normal wait for signal, then release seanhalle@273: // ACK lock, to let end-process know it can free the process struct seanhalle@273: pthread_mutex_lock( &(process->doneAckLock) ); seanhalle@273: seanhalle@273: pthread_mutex_lock( &(process->doneLock) ); seanhalle@273: while( process->executionIsComplete != TRUE ) seanhalle@273: { seanhalle@273: pthread_cond_wait( &(process->doneCond), seanhalle@273: &(process->doneLock) ); seanhalle@273: } seanhalle@273: pthread_mutex_unlock( &(process->doneLock) ); seanhalle@273: result = process->resultToReturn; seanhalle@273: seanhalle@273: //now send "ACK" signal to process_end Fn, that it may proceed seanhalle@273: pthread_mutex_unlock( &(process->doneAckLock) ); seanhalle@273: seanhalle@273: return result; seanhalle@273: //TODO: BUG? -- can process be created and end, before this acquires the seanhalle@273: // first lock? Can see some rare code that creates a bunch, before getting seanhalle@273: // to waiting.. leave for now.. pain to fix.. seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@276: /*This should only be called from main. It makes the OS thread that is animating seanhalle@276: * main to suspend until the process completes shutdown. seanhalle@276: */ seanhalle@273: void seanhalle@273: PR__wait_for_process_to_end( PRProcess *process ) seanhalle@273: { seanhalle@276: process->hasWaitingToEnd = TRUE; seanhalle@276: seanhalle@273: #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE seanhalle@273: // call the one and only core ctlr (sequential version), in the main thread. seanhalle@276: if( process->executionIsComplete ) seanhalle@276: return; seanhalle@276: else seanhalle@276: { coreCtlr_Seq( NULL ); seanhalle@276: flushRegisters(); //Not sure why here, but leaving to be safe seanhalle@276: process->executionIsComplete = TRUE; seanhalle@276: } seanhalle@273: #else seanhalle@273: //First get the "ACK" lock, then do normal wait for signal, then release seanhalle@273: // ACK lock, to let end-process know it can free the process struct seanhalle@273: pthread_mutex_lock( &(process->doneAckLock) ); seanhalle@273: pthread_mutex_lock( &(process->doneLock) ); seanhalle@273: while( process->executionIsComplete != TRUE ) seanhalle@273: { seanhalle@273: pthread_cond_wait( &(process->doneCond), seanhalle@273: &(process->doneLock) ); seanhalle@273: } seanhalle@273: pthread_mutex_unlock( &(process->doneLock) ); seanhalle@273: //now send "ACK" signal to process_end Fn, that it may proceed seanhalle@273: pthread_mutex_unlock( &(process->doneAckLock) ); seanhalle@273: seanhalle@273: //TODO: BUG? -- can process be created and end, before this acquires the seanhalle@273: // first lock? Can see some rare code that creates a bunch, before getting seanhalle@273: // to waiting.. leave for now.. pain to fix.. seanhalle@273: #endif seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: void seanhalle@273: PR__wait_for_all_activity_to_end() seanhalle@273: { seanhalle@276: #ifdef DEBUG__TURN_ON_SEQUENTIAL_MODE seanhalle@276: //In sequential mode, can't reach this call unless all activity has seanhalle@276: // already completed, so just return. seanhalle@276: return; seanhalle@276: #else seanhalle@273: pthread_mutex_lock( &(_PRTopEnv->activityDoneLock) ); seanhalle@273: while( !(_PRTopEnv->allActivityIsDone) ) seanhalle@273: { seanhalle@273: pthread_cond_wait( &(_PRTopEnv->activityDoneCond), seanhalle@273: &(_PRTopEnv->activityDoneLock) ); seanhalle@273: } seanhalle@273: pthread_mutex_unlock( &(_PRTopEnv->activityDoneLock) ); seanhalle@276: #endif seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: /*This info is retrieved by PRServ's "give environ string" function seanhalle@273: *These Fn s are meant to be called from main, or possibly the seed slave. seanhalle@273: */ seanhalle@273: void seanhalle@273: PR__set_app_info( char *info ) seanhalle@273: { int32 len; seanhalle@273: char *copy; seanhalle@273: len = strlen(info) +1; seanhalle@273: copy = PR_int__malloc(len); seanhalle@273: strcpy(copy, info); seanhalle@273: _PRTopEnv->metaInfo->appInfo = copy; seanhalle@273: } seanhalle@273: void seanhalle@273: PR__set_input_info( char *info ) seanhalle@273: { int32 len; seanhalle@273: char *copy; seanhalle@273: len = strlen(info) +1; seanhalle@273: copy = PR_int__malloc(len); seanhalle@273: strcpy(copy, info); seanhalle@273: _PRTopEnv->metaInfo->inputInfo = copy; seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: seanhalle@273: seanhalle@273: //========================== SHUT DOWN =========================== seanhalle@273: seanhalle@273: /*This is called from the main thread, and causes PR's OS threads to stop seanhalle@273: * then cleans up any memory allocated by PR from the OS. seanhalle@273: * seanhalle@273: *The main thread has a separate call it can use to wait for all work to seanhalle@273: * finish, so when this is called, it just shuts down, whether there's seanhalle@273: * unfinished work or not. seanhalle@273: * seanhalle@273: *However, cores that are performing work won't see this shutdown until seanhalle@273: * they finish their current work-unit. seanhalle@273: */ seanhalle@273: void seanhalle@273: PR__shutdown() seanhalle@273: { int32 coreIdx; seanhalle@273: seanhalle@273: PR_SS__shutdown_OS_threads(); seanhalle@273: seanhalle@273: //wait for the OS threads to exit seanhalle@273: for( coreIdx=0; coreIdx < NUM_CORES; coreIdx++ ) seanhalle@273: { seanhalle@273: pthread_join( coreCtlrThdHandles[coreIdx], NULL ); seanhalle@273: } seanhalle@273: seanhalle@273: //Before getting rid of everything, print out any measurements made seanhalle@273: PR_SS__print_out_measurements(); seanhalle@273: seanhalle@273: //free all memory allocated from the OS seanhalle@273: PR_SS__cleanup_at_end_of_shutdown(); seanhalle@273: } seanhalle@273: seanhalle@273: seanhalle@273: