CsCheck/Check.cs
/// <summary>Sample model-based operations on a random initial state concurrently.
/// The result is compared against the result of the possible sequential permutations.
/// At least one of these permutations result must be equal for the concurrency to have been linearized successfully.
/// If not the failing initial state and sequence will be shrunk down to the shortest and simplest.</summary>
/// <param name="initial">The initial state generator.</param>
/// <param name="operations">The operation generators that can act on the state concurrently.</param>
/// <param name="equal">A function to check if the two states are the same (default Check.Equal).</param>
/// <param name="seed">The initial seed to use for the first iteration.</param>
/// <param name="iter">The number of iterations to run in the sample (default 100).</param>
/// <param name="time">The number of seconds to run the sample.</param>
/// <param name="threads">The number of threads to run the sample on (default number logical CPUs).</param>
/// <param name="print">A function to convert the state to a string for error reporting (default Check.Print).</param>
/// <param name="replay">The number of times to retry the seed to reproduce an initial fail (default 100).</param>
Sample model-based operations on a random initial state concurrently.
The result is compared against the result of the possible sequential permutations.
At least one of these permutations result must be equal for the concurrency to have been linearized successfully.
If not the failing initial state and sequence will be shrunk down to the shortest and simplest.
Sample model - based operations on a random initial state concurrently . The result is compared against the result of the possible sequential permutations . At least one of these permutations result must be equal for the concurrency to have been linearized successfully . If not the failing initial state and sequence will be shrunk down to the shortest and simplest .
public static void SampleConcurrent<T>(this Gen<T> initial, GenOperation<T>[] operations,
            Func<T, T, bool> equal = null, string seed = null, long iter = -1, int time = -1, int threads = -1, Func<T, string> print = null, int replay = -1)
        {
            if (equal is null) equal = Equal;
            if (seed is null) seed = Seed;
            if (iter == -1) iter = Iter;
            if (time == -1) time = Time;
            if (threads == -1) threads = Threads;
            if (print is null) print = Print;
            if (replay == -1) replay = Replay;
            int[] replayThreads = null;
            if (seed is not null && seed.Contains("["))
            {
                int i = seed.IndexOf('[');
                int j = seed.IndexOf(']', i + 1);
                replayThreads = seed.Substring(i + 1, j - i - 1).Split(',').Select(int.Parse).ToArray();
                seed = seed.Substring(0, i);
            }
            var opNameActions = new Gen<(string, Action<T>)>[operations.Length];
            for (int i = 0; i < operations.Length; i++)
            {
                var op = operations[i];
                var opName = "Op" + i;
                opNameActions[i] = op.AddOpNumber ? op.Select(t => (opName + t.Item1, t.Item2)) : op;
            }
            bool firstIteration = true;
            Gen.Create((PCG pcg, Size min, out Size size) =>
            {
                var stream = pcg.Stream;
                var seed = pcg.Seed;
                return (initial.Generate(pcg, null, out size), stream, seed);
            })
            .Select(Gen.OneOf(opNameActions).Array[1, MAX_CONCURRENT_OPERATIONS].SelectTuple(ops => Gen.Int[1, Math.Min(threads, ops.Length)]), (a, b) =>
                new ConcurrentData<T> { State = a.Item1, Stream = a.stream, Seed = a.seed, Operations = b.V0, Threads = b.V1 }
            )
            .Sample(cd =>
            {
                bool linearizable = false;
                do
                {
                    try
                    {
                        if (replayThreads is null)
                            Run(cd.State, cd.Operations, cd.Threads, cd.ThreadIds = new int[cd.Operations.Length]);
                        else
                            RunReplay(cd.State, cd.Operations, cd.Threads, cd.ThreadIds = replayThreads);
                    }
                    catch (Exception e)
                    {
                        cd.Exception = e;
                        break;
                    }
                    System.Threading.Tasks.Parallel.ForEach(Permutations(cd.ThreadIds, cd.Operations), (sequence, state) =>
                    {
                        var linearState = initial.Generate(new PCG(cd.Stream, cd.Seed), null, out _);
                        try
                        {
                            Run(linearState, sequence, 1);
                            if (equal(cd.State, linearState))
                            {
                                linearizable = true;
                                state.Stop();
                            }
                        }
                        catch { state.Stop(); }
                    });
                } while (linearizable && firstIteration && seed != null && --replay > 0);
                firstIteration = false;
                return linearizable;
            }, seed, iter, time, threads: 1,
            p =>
            {
                if (p == null) return "";
                var sb = new StringBuilder();
                sb.Append("\n   Operations: ").Append(Print(p.Operations.Select(i => i.Item1).ToList()));
                sb.Append("\n   On Threads: ").Append(Print(p.ThreadIds));
                sb.Append("\nInitial state: ").Append(print(initial.Generate(new PCG(p.Stream, p.Seed), null, out _)));
                sb.Append("\n  Final state: ").Append(p.Exception is not null ? p.Exception.ToString() : print(p.State));
                bool first = true;
                foreach (var sequence in Permutations(p.ThreadIds, p.Operations))
                {
                    var linearState = initial.Generate(new PCG(p.Stream, p.Seed), null, out _);
                    string result;
                    try
                    {
                        Run(linearState, sequence, 1);
                        result = print(linearState);
                    }
                    catch (Exception e)
                    {
                        result = e.ToString();
                    }
                    sb.Append(first ? "\n   Linearized: " : "\n             : ");
                    sb.Append(Print(sequence.Select(i => i.Item1).ToList()));
                    sb.Append(" -> ");
                    sb.Append(result);
                    first = false;
                }
                return sb.ToString();
            });
        }
public static void SampleConcurrent < T > ( this Gen < T > initial , GenOperation < T > [ ] operations , Func < T , T , bool > equal = null , string seed = null , long iter = - 1 , int time = - 1 , int threads = - 1 , Func < T , string > print = null , int replay = - 1 ) { if ( equal is null ) equal = Equal ; if ( seed is null ) seed = Seed ; if ( iter == - 1 ) iter = Iter ; if ( time == - 1 ) time = Time ; if ( threads == - 1 ) threads = Threads ; if ( print is null ) print = Print ; if ( replay == - 1 ) replay = Replay ; int [ ] replayThreads = null ; if ( seed is not null && seed . Contains ( " " ) ) { int i = seed . IndexOf ( ' ' ) ; int j = seed . IndexOf ( ' ' , i + 1 ) ; replayThreads = seed . Substring ( i + 1 , j - i - 1 ) . Split ( ' ' ) . Select ( int . Parse ) . ToArray ( ) ; seed = seed . Substring ( 0 , i ) ; } var opNameActions = new Gen < ( string , Action < T > ) > [ operations . Length ] ; for ( int i = 0 ; i < operations . Length ; i ++ ) { var op = operations [ i ] ; var opName = " " + i ; opNameActions [ i ] = op . AddOpNumber ? op . Select ( t => ( opName + t . Item1 , t . Item2 ) ) : op ; } bool firstIteration = true ; Gen . Create ( ( PCG pcg , Size min , out Size size ) => { var stream = pcg . Stream ; var seed = pcg . Seed ; return ( initial . Generate ( pcg , null , out size ) , stream , seed ) ; } ) . Select ( Gen . OneOf ( opNameActions ) . Array [ 1 , MAX_CONCURRENT_OPERATIONS ] . SelectTuple ( ops => Gen . Int [ 1 , Math . Min ( threads , ops . Length ) ] ) , ( a , b ) => new ConcurrentData < T > { State = a . Item1 , Stream = a . stream , Seed = a . seed , Operations = b . V0 , Threads = b . V1 } ) . Sample ( cd => { bool linearizable = false ; do { try { if ( replayThreads is null ) Run ( cd . State , cd . Operations , cd . Threads , cd . ThreadIds = new int [ cd . Operations . Length ] ) ; else RunReplay ( cd . State , cd . Operations , cd . Threads , cd . ThreadIds = replayThreads ) ; } catch ( Exception e ) { cd . Exception = e ; break ; } System . Threading . Tasks . Parallel . ForEach ( Permutations ( cd . ThreadIds , cd . Operations ) , ( sequence , state ) => { var linearState = initial . Generate ( new PCG ( cd . Stream , cd . Seed ) , null , out _ ) ; try { Run ( linearState , sequence , 1 ) ; if ( equal ( cd . State , linearState ) ) { linearizable = true ; state . Stop ( ) ; } } catch { state . Stop ( ) ; } } ) ; } while ( linearizable && firstIteration && seed != null && -- replay > 0 ) ; firstIteration = false ; return linearizable ; } , seed , iter , time , threads : 1 , p => { if ( p == null ) return " " ; var sb = new StringBuilder ( ) ; sb . Append ( " \n " ) . Append ( Print ( p . Operations . Select ( i => i . Item1 ) . ToList ( ) ) ) ; sb . Append ( " \n " ) . Append ( Print ( p . ThreadIds ) ) ; sb . Append ( " \n " ) . Append ( print ( initial . Generate ( new PCG ( p . Stream , p . Seed ) , null , out _ ) ) ) ; sb . Append ( " \n " ) . Append ( p . Exception is not null ? p . Exception . ToString ( ) : print ( p . State ) ) ; bool first = true ; foreach ( var sequence in Permutations ( p . ThreadIds , p . Operations ) ) { var linearState = initial . Generate ( new PCG ( p . Stream , p . Seed ) , null , out _ ) ; string result ; try { Run ( linearState , sequence , 1 ) ; result = print ( linearState ) ; } catch ( Exception e ) { result = e . ToString ( ) ; } sb . Append ( first ? " \n " : " \n " ) ; sb . Append ( Print ( sequence . Select ( i => i . Item1 ) . ToList ( ) ) ) ; sb . Append ( " " ) ; sb . Append ( result ) ; first = false ; } return sb . ToString ( ) ; } ) ; }
Sample model-based operations on a random initial state concurrently.
Sample model - based operations on a random initial state concurrently .
Gen<T>
this
operations
GenOperation<T>[]
The operation generators that can act on the state concurrently.
The operation generators that can act on the state concurrently .
equal
Func<T, T, bool>
A function to check if the two states are the same (default Check.Equal).
A function to check if the two states are the same ( default Check . Equal ) .
seed
string
The initial seed to use for the first iteration.
The initial seed to use for the first iteration .
iter
long
The number of iterations to run in the sample (default 100).
The number of iterations to run in the sample ( default 100 ) .
time
int
The number of seconds to run the sample.
The number of seconds to run the sample .
threads
int
The number of threads to run the sample on (default number logical CPUs).
The number of threads to run the sample on ( default number logical CPUs ) .
print
Func<T, string>
A function to convert the state to a string for error reporting (default Check.Print).
A function to convert the state to a string for error reporting ( default Check . Print ) .
replay
int
The number of times to retry the seed to reproduce an initial fail (default 100).
The number of times to retry the seed to reproduce an initial fail ( default 100 ) .
initial
The initial state generator.
The initial state generator .