/// <summary> /// Classifies how the transition set must be updated. /// </summary> private void ClassifyActivatedFaults(FaultSet activatedFaults, int faultIndex, out bool addTransition, out bool addFaults, out bool cleanupTransitions) { addFaults = false; cleanupTransitions = false; // Basic invariant of the fault list: it contains only sets of activation-minimal faults while (faultIndex != -1) { var faultSet = &_faults[faultIndex]; faultIndex = faultSet->NextSet; // If the fault set is a subset of the activated faults, the current transition is not activation-minimal; // we can therefore safely ignore the transition; due to the list invariant, none of the remaining // fault sets in the list can be a superset of the activated faults because then the current fault set // would also be a subset of that other fault set, violating the invariant if (faultSet->ActivatedFaults.IsSubsetOf(activatedFaults)) { addTransition = faultSet->ActivatedFaults == activatedFaults; return; } // If at least one of the previously added transitions that we assumed to be activation-minimal is // in fact not activation-minimal, we have to clean up the transition set if (activatedFaults.IsSubsetOf(faultSet->ActivatedFaults)) { cleanupTransitions = true; } } // If we get here, we must add the faults and the transition addTransition = true; addFaults = true; }
/// <summary> /// Adds the <paramref name="successorState" /> to the transition set if neccessary, reached using the /// <paramref name="activatedFaults" />. /// </summary> /// <param name="successorState">The successor state that should be added.</param> /// <param name="activatedFaults">The faults activated by the transition to reach the state.</param> private bool Add(byte *successorState, FaultSet activatedFaults) { var hash = MemoryBuffer.Hash(successorState, _stateVectorSize, 0); for (var i = 1; i < ProbeThreshold; ++i) { var stateHash = MemoryBuffer.Hash((byte *)&hash, sizeof(int), i * 8345723) % _capacity; var faultIndex = _lookup[stateHash]; // If we don't know the state yet, set everything up and add the transition if (faultIndex == -1) { _successors.Add((uint)stateHash); AddFaultMetadata(stateHash, activatedFaults, -1); MemoryBuffer.Copy(successorState, _hashedStateMemory + stateHash * _stateVectorSize, _stateVectorSize); return(true); } // If there is a hash conflict, try again if (!MemoryBuffer.AreEqual(successorState, _hashedStateMemory + stateHash * _stateVectorSize, _stateVectorSize)) { continue; } // The transition has an already-known target state; it might have to be added or invalidate previously found transitions return(UpdateTransitions(stateHash, activatedFaults, faultIndex)); } throw new OutOfMemoryException( "Failed to find an empty hash table slot within a reasonable amount of time. Try increasing the successor state capacity."); }
/// <summary> /// Adds a transition to the <paramref name="model" />'s current state. /// </summary> /// <param name="model">The model the transition should be added for.</param> public void Add(RuntimeModel model) { ++ComputedTransitionCount; // 1. Serialize the model's computed state; that is the successor state of the transition's source state // modulo any changes resulting from notifications of fault activations var successorState = _targetStateMemory + _stateVectorSize * Count; var activatedFaults = FaultSet.FromActivatedFaults(model.Faults); model.Serialize(successorState); // 2. Make sure the transition we're about to add is activation-minimal if (!Add(successorState, activatedFaults)) { return; } // 3. Execute fault activation notifications and serialize the updated state if necessary if (model.NotifyFaultActivations()) { model.Serialize(successorState); } // 4. Store the transition _transitions[Count] = new Transition { TargetState = successorState, Formulas = new StateFormulaSet(_formulas), IsValid = true, }; ++Count; }
/// <summary> /// Initializes a new instance. /// </summary> /// <param name="serializedData">The serialized data describing the model.</param> /// <param name="stateHeaderBytes"> /// The number of bytes that should be reserved at the beginning of each state vector for the model checker tool. /// </param> internal RuntimeModel(SerializedRuntimeModel serializedData, int stateHeaderBytes = 0) { Requires.That(serializedData.Model != null, "Expected a valid model instance."); var buffer = serializedData.Buffer; var rootComponents = serializedData.Model.Roots; var objectTable = serializedData.ObjectTable; var formulas = serializedData.Formulas; Requires.NotNull(buffer, nameof(buffer)); Requires.NotNull(rootComponents, nameof(rootComponents)); Requires.NotNull(objectTable, nameof(objectTable)); Requires.NotNull(formulas, nameof(formulas)); Requires.That(stateHeaderBytes % 4 == 0, nameof(stateHeaderBytes), "Expected a multiple of 4."); Model = serializedData.Model; SerializedModel = buffer; RootComponents = rootComponents.Cast <Component>().ToArray(); Faults = objectTable.OfType <Fault>().Where(fault => fault.Activation == Activation.Nondeterministic && fault.IsUsed).ToArray(); ActivationSensitiveFaults = Faults.Where(fault => fault.RequiresActivationNotification).ToArray(); StateFormulas = objectTable.OfType <StateFormula>().ToArray(); Formulas = formulas; // Create a local object table just for the objects referenced by the model; only these objects // have to be serialized and deserialized. The local object table does not contain, for instance, // the closure types of the state formulas var objects = Model.ReferencedObjects; var deterministicFaults = objectTable.OfType <Fault>().Where(fault => fault.Activation != Activation.Nondeterministic); _serializedObjects = new ObjectTable(objects.Except(deterministicFaults, ReferenceEqualityComparer <object> .Default)); Objects = objectTable; StateVectorLayout = SerializationRegistry.Default.GetStateVectorLayout(Model, _serializedObjects, SerializationMode.Optimized); _deserialize = StateVectorLayout.CreateDeserializer(_serializedObjects); _serialize = StateVectorLayout.CreateSerializer(_serializedObjects); _restrictRanges = StateVectorLayout.CreateRangeRestrictor(_serializedObjects); _stateHeaderBytes = stateHeaderBytes; PortBinding.BindAll(objectTable); _choiceResolver = new ChoiceResolver(objectTable); ConstructionState = new byte[StateVectorSize]; fixed(byte *state = ConstructionState) { Serialize(state); _restrictRanges(); } FaultSet.CheckFaultCount(Faults.Length); StateFormulaSet.CheckFormulaCount(StateFormulas.Length); }
/// <summary> /// Adds the fault metadata of the current transition. /// </summary> private void AddFaultMetadata(long stateHash, FaultSet activatedFaults, int nextSet) { if (_nextFaultIndex >= _capacity) { throw new OutOfMemoryException("Out of memory. Try increasing the successor state capacity."); } _faults[_nextFaultIndex] = new FaultSetInfo { ActivatedFaults = activatedFaults, NextSet = nextSet, Transition = &_transitions[Count] }; _lookup[stateHash] = _nextFaultIndex; _nextFaultIndex++; }
/// <summary> /// Adds the current transition if it is activation-minimal. Previously found transition might have to be removed if they are /// no longer activation-minimal. /// </summary> private bool UpdateTransitions(long stateHash, FaultSet activatedFaults, int faultIndex) { bool addTransition; bool addFaults; bool cleanupTransitions; ClassifyActivatedFaults(activatedFaults, faultIndex, out addTransition, out addFaults, out cleanupTransitions); if (cleanupTransitions) { CleanupTransitions(activatedFaults, faultIndex, stateHash); } if (addFaults) { AddFaultMetadata(stateHash, activatedFaults, _lookup[stateHash]); } return(addTransition); }
/// <summary> /// Removes all transitions that are no longer activation minimal due to the current transition. /// </summary> private void CleanupTransitions(FaultSet activatedFaults, int faultIndex, long stateHash) { var current = faultIndex; var nextPointer = &_lookup[stateHash]; while (current != -1) { var faultSet = &_faults[current]; // Remove the fault set and the corresponding transition if it is a proper subset of the activated faults if (activatedFaults.IsSubsetOf(faultSet->ActivatedFaults) && activatedFaults != faultSet->ActivatedFaults) { faultSet->Transition->IsValid = false; *nextPointer = faultSet->NextSet; } if (nextPointer != &_lookup[stateHash]) { nextPointer = &faultSet->NextSet; } current = faultSet->NextSet; } }