public TransientBuffer(KeySignature <string> sig, DynamicBuffers parent) : base(parent._parent._calculateAndStoreFromInputAndAsyncTermsOptions) { Log.Trace("Constructor Starting"); this._parent = parent; Log.Trace($"ElementSet: {sig.Longest().ToString()}"); Log.Trace("Creating _buffer"); _buffer = new BufferBlock <IInternalMessage <string> >(); Log.Trace("Registering _buffer"); RegisterChild(_buffer); // critical section // iterate each individual element of the sig, and get those that are not already present in the COD FetchingIndividualElementsOfTerm1 foreach (var element in sig.IndividualElements) { if (!parent._parent._calculateAndStoreFromInputAndAsyncTermsObservableData.FetchingIndividualElementsOfTerm1.ContainsKey(element)) { // For each element that is not already being fetched, start the async task to fetch it Log.Trace($"Fetching AsyncWebGet for {element} and storing the task in FetchingIndividualElementsOfTerm1 indexed by {element}"); // call the async function that fetches the information for each individual element in the elementSet // record the individual element and it's corresponding task in the FetchingIndividualElementsOfTerm1 parent._parent._calculateAndStoreFromInputAndAsyncTermsObservableData.FetchingIndividualElementsOfTerm1[element] = parent._parent._webGet.AsyncWebGet <double>(element); } } // Record into FetchingElementSetsOfTerm1 the sig.Longest as key, and for the data, // create a COD whose keys are the keys of sig.IndividualElements in the FetchingElementSetsOfTerm1 Log.Trace("Creating new concurrent dictionary"); var x = new ConcurrentObservableDictionary <string, byte>(); foreach (var element in sig.IndividualElements) { Log.Trace($"Creating an entry for {element}"); x[element] = default; } Log.Trace($"Creating an entry for FetchingElementSetsOfTerm1. Key is {sig.Longest()} data is x"); parent._parent._calculateAndStoreFromInputAndAsyncTermsObservableData.FetchingElementSetsOfTerm1[sig.Longest()] = x; // If the asyncFetchCheckTimer is not enabled, enable it now. if (!parent._parent.asyncFetchCheckTimer.Enabled) { parent._parent.asyncFetchCheckTimer.Enabled = true; } Log.Trace("Constructor Finished"); }
// Constructor public CalculateAndStoreFromInputAndAsyncTerms(CalculateAndStoreFromInputAndAsyncTermsObservableData calculateAndStoreFromInputAndAsyncTermsObservableData, IWebGet webGet, CalculateAndStoreFromInputAndAsyncTermsOptions calculateAndStoreFromInputAndAsyncTermsOptions) : base(calculateAndStoreFromInputAndAsyncTermsOptions) { Log.Trace("Constructor starting"); _calculateAndStoreFromInputAndAsyncTermsObservableData = calculateAndStoreFromInputAndAsyncTermsObservableData; _webGet = webGet; _calculateAndStoreFromInputAndAsyncTermsOptions = calculateAndStoreFromInputAndAsyncTermsOptions; // Create a place to store the TransientBuffers that buffer messages while waiting For all elements that make up the ElementSets Of Term1 to finish fetching _transientBuffersForElementSetsOfTerm1 = new ConcurrentDictionary <string, Dataflow <IInternalMessage <string>, IInternalMessage <string> > >(); // foreach IInputMessage<TKeyTerm1,TValueTerm1>, create an internal message that adds the hashset for the terms1 and the bool used by the routing predicate Log.Trace("Creating _bAccepter"); _bAccepter = new TransformBlock <IInputMessage <string, double>, IInternalMessage <string> >(_input => { Log.Trace("Accepter received IInputMessage"); // ToDo also check on the async tasks check when an upstream completion occurs // ToDo add exception handling to ensure the tasks, as well as the async method's resources, are released if any blocks in the dataflow fault // create a HashSet from the set of keys found in terms1 KeySignature <string> sig = new KeySignature <string>(_input.Value.terms1.Keys); // Is the sig.largest in the ElementSetsOfTerm1Ready dictionary? set the output bool accordingly bool isReadyToCalculate = _calculateAndStoreFromInputAndAsyncTermsObservableData.ElementSetsOfTerm1Ready.ContainsKey(sig.Longest()); // Pass the message along to the next block, which will be either the _bSolveStore, or the _bDynamicBuffers return(new InternalMessage <string>((_input.Value.k1, _input.Value.k2, _input.Value.terms1, sig, isReadyToCalculate))); }).ToDataflow(); // this block accepts messages where isReadyToCalculate is false, and buffers them Log.Trace("Creating _bDynamicBuffers"); _bDynamicBuffers = new DynamicBuffers(this); // The terminator block performs both the Solve and the Store operations Log.Trace("Creating _bSolveStore"); _bSolveStore = new ActionBlock <IInternalMessage <string> >(_input => { Log.Trace($"_bSolveStore received InternalMessage having signature {_input.Value.sig.Longest()}"); // solve the equation for the input and all terms var r1 = 0.0; foreach (var kvp in _input.Value.terms1) { r1 += kvp.Value / _calculateAndStoreFromInputAndAsyncTermsObservableData.FetchedIndividualElementsOfTerm1[kvp.Key]; } // Store the results value calculateAndStoreFromInputAndAsyncTermsObservableData.RecordR(_input.Value.k1, _input.Value.k2, Convert.ToDecimal(r1)); }).ToDataflow(calculateAndStoreFromInputAndAsyncTermsOptions); #region create asyncFetchCheckTimer and connect callback // Create a timer that is used to check on the async fetch tasks, the async fetch tasks check-for-completion loop timer // the timer has its interval from the options passed into this constructor, it will restart and the event handler will stop the timer and start the timer each time // ToDo add the timer that checks on the health of the async fetch tasks check-for-completion loop every DefaultAsyncFetchTimeout interval, expecting it to provide a heartbeat, // The Cleanup method will call this timers Dispose method // the event handler's job is to call CheckAsyncTasks which will check for completed fetches and link the child Transient buffers to the _bSolveStore Log.Trace("creating and starting the asyncFetchCheckTimer"); asyncFetchCheckTimer = new Timer(_calculateAndStoreFromInputAndAsyncTermsOptions.AsyncFetchTimeInterval.TotalMilliseconds); asyncFetchCheckTimer.AutoReset = true; // set the event handler (callback) for this timer to the function for async fetch tasks check-for-completion loop asyncFetchCheckTimer.Elapsed += new ElapsedEventHandler(asyncFetchCheckTimer_Elapsed); asyncFetchCheckTimer.Start(); #endregion create asyncFetchCheckTimer and connect callback _bAccepter.Name = "_bAccepter"; _bSolveStore.Name = "_bSolveStore"; _bDynamicBuffers.Name = "_bDynamicBuffers"; // Link the data flow Log.Trace("Linking dataflow between blocks"); // Link _bAccepter to _bSolveStore when the InternalMessage.Value has isReadyToCalculate = true _bAccepter.LinkTo(_bSolveStore, internalMessage => internalMessage.Value.isReadyToCalculate); // Link _bAccepter to _bDynamicBuffers when the InternalMessage.Value has isReadyToCalculate = false _bAccepter.LinkTo(_bDynamicBuffers, internalMessage => !internalMessage.Value.isReadyToCalculate); // data flow linkage of the dynamically created TransientBuffer children to the _bSolveStore is complex and handled elsewhere // Link the completion tasks Log.Trace("Linking completion between blocks"); _bDynamicBuffers.RegisterDependency(_bAccepter); _bSolveStore.RegisterDependency(_bAccepter); _bSolveStore.RegisterDependency(_bDynamicBuffers); // Completion linkage of the dynamically created TransientBuffer children to the _bSolveStore is complex and handled elsewhere Log.Trace("Registering Children"); this.RegisterChild(_bAccepter); this.RegisterChild(_bSolveStore); this.RegisterChild(_bDynamicBuffers); // set the InputBlock for this dataflow graph to be the InputBlock of the _acceptor this._headBlock = _bAccepter.InputBlock; // ToDo: add an optional constructor parameter that supplies an initial list of elements that can start pre-fetching for each term Log.Trace("Constructor Finished"); }