Skip to content

Evaled returns a value computed at another flow's precision: pi read at 100 digits gives 199 #1505

Description

@Rafael-SOWNet

A value evaluated under a raised precision in one flow is returned to another flow that is still at the default precision:

var evaluated = new ManualResetEventSlim();
var release = new ManualResetEventSlim();
var other = Task.Run(() =>
{
    using var _ = MathS.Settings.DecimalPrecisionContext.Set(new EContext(200, ERounding.HalfUp, -1000, 1000, false));
    var high = MathS.pi.Evaled;
    evaluated.Set();
    release.Wait();
});
evaluated.Wait();
Console.WriteLine(MathS.Settings.DecimalPrecisionContext.Value.Precision);   // 100
Console.WriteLine(MathS.pi.Evaled.ToString().Length - 2);                    // 199 digits
release.Set();

Once the other flow's scope closes, the same read gives 99 digits again. This happens on master (c9f688b7) in three runs out of three.

It is what failed #1504's first CI run: DocumentationExamplesRunTest.EveryDocumentedExamplePrintsWhatItSaysItPrints got pi at more than 200 digits in the example on MathS.i, while the test suite ran other classes in parallel. On rerun it passed.

The cause is in Evaled's cache. The value is stamped with EvaluationEpoch.Current, a counter shared by the whole process, while the precision it was computed at belongs to one flow (Setting<T> is an AsyncLocal). One flow opens a precision scope, which advances the epoch, and evaluates a node another flow shares, such as the static MathS.pi. From then until something advances the epoch again, every flow gets that value back. EvaluationEpoch's remark that a change on another thread costs "a recomputation and never a stale value" holds for the change itself, but not for a computation made after the change.

The fix I'd propose is to stamp the cached value with the precision frame it was computed under, not only the epoch. Every Setting frame already has a process-unique Id. A flow in another precision scope would then recompute instead of reading. The cost is one AsyncLocal read per Evaled, against a static read now, so I'd measure EvalEasy and the others before and after.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions