private EvaluationResult QualifiedEvaluateWithCycleDetection(ref object value, ImmutableContextBase context, ModuleLiteral env, Evaluation currentEvaluation, ref MutableContextFactory factory) { // Someone else is already busy evaluating this thunk. Let's wait... EvaluationStatus result; var cycleDetector = context.FrontEndHost.CycleDetector; if (cycleDetector != null) { using (cycleDetector.AddValuePromiseChain( valuePromiseChainGetter: () => GetValuePromiseChain(context, env), cycleAnnouncer: () => currentEvaluation.Cancel(EvaluationStatus.Cycle))) { currentEvaluation.Wait(cycleDetector); result = currentEvaluation.Result; if ((result & EvaluationStatus.Value) != 0) { var currentValue = Volatile.Read(ref value); Contract.Assert(currentValue is EvaluationResult); return((EvaluationResult)currentValue); } } } else { currentEvaluation.Wait(); result = currentEvaluation.Result; if ((result & EvaluationStatus.Value) != 0) { var currentValue = Volatile.Read(ref value); Contract.Assert(currentValue is EvaluationResult); return((EvaluationResult)currentValue); } } // Evaluation got canceled --- we hit a cycle (or deadlock)! if ((result & EvaluationStatus.Cycle) != 0) { context.Errors.ReportCycle(env, Expression.Location, factory.ContextName); currentEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } // Evaluation crashed, which means that Expression.Eval had failed, and had thrown an exception. if ((result & EvaluationStatus.Crash) != 0) { // Crash has been reported at the crash site. currentEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } return(EvaluationResult.Error); }
private EvaluationResult QualifiedEvaluate( ref object value, ImmutableContextBase context, ModuleLiteral env, EvaluationStackFrame args, ref MutableContextFactory factory) { // do we have a real value yet? var currentValue = Volatile.Read(ref value); var currentEvaluation = currentValue as Evaluation; if (currentValue != currentEvaluation) { Contract.Assert(currentValue is EvaluationResult); // so it's not null, and it's not an evaluation => we have a real value! return((EvaluationResult)currentValue); } // no real value yet, let's try to create a new evaluation if (currentEvaluation == null && !factory.ForceWaitForResult) { var newEvaluation = new Evaluation( context.EvaluatorConfiguration.CycleDetectorStartupDelay, context.EvaluatorConfiguration.CycleDetectorIncreasePriorityDelay); currentValue = Interlocked.CompareExchange(ref value, newEvaluation, null); currentEvaluation = currentValue as Evaluation; if (currentValue != currentEvaluation) { Contract.Assert(currentValue is EvaluationResult); // so it's not null, and it's not an evaluation => we have a real value! (and the CompareExchange must have failed) return((EvaluationResult)currentValue); } if (currentValue == null) { using (var newLocalMutableContext = factory.Create(context)) { try { var newValue = Expression.Eval(newLocalMutableContext, env, args); // Evaluation got canceled --- we hit a cycle (or deadlock)! if ((newEvaluation.Result & EvaluationStatus.Cycle) != 0) { newLocalMutableContext.Errors.ReportCycle(env, Expression.Location); newEvaluation.SetValue(ref value, EvaluationResult.Error); return(EvaluationResult.Error); } newEvaluation.SetValue(ref value, newValue); return(newValue); } catch { // No actual value was ever set --- this means that Expression.Eval failed, and most likely threw an exception! // Let's propagate this result, so that anyone else waiting for us gets unblocked. newEvaluation.Cancel(EvaluationStatus.Crash); throw; } finally { Contract.Assert(!newLocalMutableContext.HasChildren); // just before the newly created context get disposed, we want to assert that all of its child contexts have already been disposed } } } // there's already an ongoing evaluation! (and the CompareExchange must have failed) we fall through... } if (factory.ForceWaitForResult) { while (currentEvaluation == null) { Thread.Sleep(TimeSpan.FromMilliseconds(10)); currentEvaluation = Volatile.Read(ref value) as Evaluation; } } return(QualifiedEvaluateWithCycleDetection(ref value, context, env, currentEvaluation, ref factory)); }