/// <summary>Creates a new instance.</summary> /// <param name="processCount">The number of process threads to create to execute the MPI program.</param> private MiniMPIProgram(int processCount) { _id = GetNextMpiRuntimeID(); _owningThreadID = Thread.CurrentThread.ManagedThreadId; _startProcessesTogether = new ManualResetEvent(false); // Create all the processes ProcessCount = processCount; _firstErroredProcessRank = -1; _state = MiniMPIProgramState.Initialized; }
/// <summary> /// Every time the runtime state changes, this method determines the /// change and performs the processing of runtime rules. /// </summary> /// <remarks> /// This method runs a continuous loop. Inside of the continuous loop, /// it waits for the runtime state to change. If all of the processes /// have finished, then it will set the state to finished and /// subsequently will wait until all of the processes join. /// A subclass of MiniMPIRuntime may define the function OnProcessRuntimeRules to describe /// what this method should do as well each time the runtime state changes. /// </remarks> private void ExecuteRuntimeLoop() { do { // TODO: Use different handles for instructions versus _stateID changes. //WaitHandle.WaitAny // Wait for the state to change before trying to do anything Runtime.BlockTillStateChanged(); // Determine the current state MiniMPIProgramState state = _state; if (state != MiniMPIProgramState.Finished) { // The only way we can detect when to finish is whether all processes have // finished. if (Thread.VolatileRead(ref _processesFinishedCount) == ProcessCount) { // The most common way the state will get set to Finished is when // all the process threads have finished. _state = state = MiniMPIProgramState.Finished; } } // Give highest priority to terminating this thread if (state == MiniMPIProgramState.Finished) { return; } // First, move any new pending instructions to the ready state // Do this just once per iteration Runtime.AddInstructionsFromPendingQueue(); // Only process rules if we are executing if (state == MiniMPIProgramState.Executing) { // The ProcessRules method should handle performing the collective abort Runtime.ProcessRules(); } } while (true); }
/// <summary> /// Starts the runtime thread and all process threads and waits for the /// runtime to finish. /// </summary> /// <exception cref="MiniMPIExecutionException">At least one spawned process thread threw an exception.</exception> private void Execute <TIMpiApi>(Action <TIMpiApi> processWork) where TIMpiApi : IMiniMPICoreAPI { // ** Make sure we've only been called once ** if (_hasExecuteBeenCalled) { throw new InvalidOperationException("Execute has already been called for this runtime instance."); } _hasExecuteBeenCalled = true; // Spawn and start the processes, but they'll wait until the next line to be // allowed to start all at the same time. SpawnWorkerProcesses(processWork); Runtime.InitializeProgram(this.Processes); _state = MiniMPIProgramState.Executing; // Signal to the processes that they may start now at the same time. // Note: The reason this is done is so that all processes start at // same time. This will allow for no favoritism to the lower-ranked // processes which are started first. _startProcessesTogether.Set(); // Execute the runtime loop ExecuteRuntimeLoop(); // Make sure that the state of the MiniMPIRuntime is set to Finished, // otherwise something went wrong. Debug.Assert(_state == MiniMPIProgramState.Finished); // Make sure each process's thread has fully completed too // TODO: Think about making the Process threads background threads. Then they'll always get aborted once the program exits. foreach (var p in Processes) { p.Thread.Join(); } // Make sure that the number of processes that finished is equal to // the number of processes created. Debug.Assert(Thread.VolatileRead(ref _processesFinishedCount) == ProcessCount); // Detect any errors // JAM: Do I need these to be a Thread.VolatileRead (p.Error, _firstErroredProcessRank)? // Or is the Interlocked.ExchangeCompare in the process threads enough? int firstErroredProcess = _firstErroredProcessRank; if (firstErroredProcess != -1) { var processErrors = Processes .Select(p => { var ex = p.Error; return(ex == null ? null : new MiniMPIProcessException(p.Rank, ex)); }) .Where(pex => pex != null) .ToArray(); throw new MiniMPIExecutionException( processErrors.Single(pex => pex.Rank == firstErroredProcess) , processErrors ); } }