private bool ForcePrecedentCalculated(ExcelReference caller, Precedents precedents) { // Ensure that all precedents have been calculated, force calculation otherwise. foreach (ReferenceFunc rf in precedents.GetFormulas()) { string funcName = rf.FuncName; if (knownAsyncFuncs_.Contains(funcName)) // Check that precedent is our known function, so we force its calculation if need. { // We just check that our function has been calculated at the specified reference. if (!calculatedFunctions_.Contains(rf)) { ExcelReference precedent = rf.ExcelReference; bool shouldForceCalculation = IsPrecedentCalcShouldForced(caller, precedent, funcName); if (shouldForceCalculation) { if (!evalStates_.ContainsKey(precedent)) { // Force evaluate precedent because we don't know its state. ExcelAsyncUtil.QueueAsMacro(() => ForceFormulaEvaluate(precedent)); } return(true); } } } } return(false); }
/// <summary> /// Starts async UDF calculation. /// Returns Scheduled/Calculating or calculated value. /// Make sure calling UDF marked with IsMacroType=true. /// </summary> public object Calc(CallArguments args) { CheckFuncName(args); MarkUdfNonVolatile(); ExcelReference caller; try { caller = GetCallerExcelReference(); } catch (Exception e) { logService_.WriteError( "Error getting reference. " + FunctionLoggerHelper.LogParameters(args.FunctionName, null), e); // TODO Cleanup return("Error getting reference."); } if (IsNewEvaluationBatchStarted) { OnEvaluatingBatchStarted(); } object[] asyncKeyArgs = GetAsyncKeyArgs(args.RawArgs, caller, batchStarted_); AsyncCallInfo asyncCallInfo = new AsyncCallInfo(args.FunctionName, asyncKeyArgs); if (!IsCalculatingTheSame(caller, asyncCallInfo) || IsScheduled(caller)) { // Check simple case. Precedent is still calculating. Precedents precedents = caller.GetPrecedents(); if (precedents.GetRefs().Any(IsCalculatingAny)) { SetEvalState(caller, EvalState.Scheduled, args, asyncCallInfo); return(Scheduled); } if (ForcePrecedentCalculated(caller, precedents)) { SetEvalState(caller, EvalState.Scheduled, args, asyncCallInfo); return(Scheduled); } } Validate(args); bool isCalculating; object result = Observe(asyncKeyArgs, caller.XlfToReference(), args, out isCalculating); if (isCalculating) { // Async call to service scheduled. SetEvalState(caller, EvalState.ObservableCreated, args, asyncCallInfo); return(Calculating); } else { // Async call to service and result are ready. SetEvalState(caller, EvalState.ObservableFinished, args, asyncCallInfo); return(result); } }