Skip to content

Commit 8d9afad

Browse files
Maintain order of logging that results from use the -traceCaching option when using parallel Boogie (boogie-org#531)
### Changes - Maintain order of logging that results from use the `-traceCaching` option when using parallel Boogie - Replace `OutputCollector` with `ConcurrentToSequentialWriteManager` that allows writing to the final TextWriter immediately when child TextWriters are written to, instead of only after calls to `WriteMoreOutput`. ### Testing - Use `%parallel-boogie` in `runtest.snapshot` which already uses `-traceCaching` - Add a test for `ConcurrentToSequentialWriteManager` - Tweak `test0/SplitOnEveryAssert.bpl` and `commandline/SplitOnEveryAssert.bpl` because the output order has improved slightly
1 parent ee3c6d6 commit 8d9afad

18 files changed

+389
-227
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
4+
namespace Microsoft.Boogie;
5+
6+
/// <summary>
7+
/// Allows grouping writes from concurrent sources before sending them to a single TextWriter.
8+
/// For each concurrent source, a TextWriter can be spawned using AppendWriter.
9+
/// </summary>
10+
public class ConcurrentToSequentialWriteManager
11+
{
12+
public TextWriter Writer { get; }
13+
private readonly Queue<SubWriter> writers = new();
14+
15+
public ConcurrentToSequentialWriteManager(TextWriter writer) {
16+
Writer = writer;
17+
}
18+
19+
private readonly object myLock = new();
20+
21+
private void Disposed() {
22+
lock (myLock) {
23+
while (writers.Count > 0 && writers.Peek().Disposed) {
24+
var disposedWriter = writers.Dequeue();
25+
Writer.Write(disposedWriter.SetTargetAndGetBuffer(null));
26+
}
27+
if (writers.Count > 0) {
28+
Writer.Write(writers.Peek().SetTargetAndGetBuffer(Writer));
29+
}
30+
}
31+
}
32+
33+
public TextWriter AppendWriter() {
34+
lock (myLock) {
35+
var target = writers.Count == 0 ? Writer : null;
36+
var result = new SubWriter(this, target);
37+
writers.Enqueue(result);
38+
return result;
39+
}
40+
}
41+
42+
class SubWriter : WriterWrapper {
43+
private readonly ConcurrentToSequentialWriteManager collector;
44+
private bool buffering;
45+
public bool Disposed { get; private set; }
46+
47+
public SubWriter(ConcurrentToSequentialWriteManager collector, TextWriter target) : base(target ?? new StringWriter()) {
48+
this.collector = collector;
49+
buffering = target == null;
50+
}
51+
52+
public string SetTargetAndGetBuffer(TextWriter newTarget) {
53+
var result = buffering ? ((StringWriter)target).ToString() : "";
54+
buffering = false;
55+
target = newTarget;
56+
return result;
57+
}
58+
59+
protected override void Dispose(bool disposing) {
60+
Disposed = true;
61+
collector.Disposed();
62+
}
63+
}
64+
}

Source/ExecutionEngine/ExecutionEngine.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using BoogiePL = Microsoft.Boogie;
1111
using System.Runtime.Caching;
1212
using System.Diagnostics;
13+
using System.Net.Mime;
1314

1415
namespace Microsoft.Boogie
1516
{
@@ -797,7 +798,7 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis
797798
Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo)
798799
{
799800
program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program);
800-
var outputCollector = new OutputCollector(stablePrioritizedImpls);
801+
var outputCollector = new ConcurrentToSequentialWriteManager(Console.Out);
801802
var outcome = PipelineOutcome.VerificationCompleted;
802803

803804
try {
@@ -830,8 +831,9 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis
830831
cts.Token.ThrowIfCancellationRequested();
831832
}
832833

834+
using var implementationWriter = outputCollector.AppendWriter();
833835
VerifyImplementation(program, stats, er, requestId, extractLoopMappingInfo, stablePrioritizedImpls,
834-
taskIndex, outputCollector, programId);
836+
taskIndex, implementationWriter, programId);
835837
ImplIdToCancellationTokenSource.TryRemove(id, out old);
836838
}
837839
finally {
@@ -885,7 +887,6 @@ private PipelineOutcome VerifyEachImplementation(Program program, PipelineStatis
885887

886888
cce.NonNull(Options.TheProverFactory).Close();
887889

888-
outputCollector.WriteMoreOutput();
889890
return outcome;
890891
}
891892

@@ -951,9 +952,8 @@ private static void CleanupRequest(string requestId)
951952

952953
private void VerifyImplementation(Program program, PipelineStatistics stats, ErrorReporterDelegate er,
953954
string requestId, Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo,
954-
Implementation[] stablePrioritizedImpls, int index, OutputCollector outputCollector, string programId)
955+
Implementation[] stablePrioritizedImpls, int index, TextWriter output, string programId)
955956
{
956-
var output = new StringWriter();
957957
Implementation impl = stablePrioritizedImpls[index];
958958

959959
printer.Inform("", output); // newline
@@ -971,7 +971,11 @@ private void VerifyImplementation(Program program, PipelineStatistics stats, Err
971971
Cache.Insert(impl, verificationResult);
972972
}
973973
}
974-
verificationResult.Emit(this, stats, er, index, outputCollector, output, impl, wasCached);
974+
verificationResult.Emit(this, stats, er, output, impl, wasCached);
975+
976+
if (verificationResult.Outcome == VCGen.Outcome.Errors || Options.Trace) {
977+
output.Flush();
978+
}
975979
}
976980

977981
private VerificationResult GetCachedVerificationResult(Implementation impl, TextWriter output)
@@ -1007,11 +1011,10 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P
10071011
try {
10081012
var cancellationToken = RequestIdToCancellationTokenSource[requestId].Token;
10091013
verificationResult.Outcome =
1010-
vcgen.VerifyImplementation(impl, out verificationResult.Errors,
1014+
vcgen.VerifyImplementation(new ImplementationRun(impl, output), out verificationResult.Errors,
10111015
out verificationResult.VCResults, requestId, cancellationToken);
10121016
if (Options.ExtractLoops && verificationResult.Errors != null) {
1013-
var vcg = vcgen as VCGen;
1014-
if (vcg != null) {
1017+
if (vcgen is VCGen vcg) {
10151018
for (int i = 0; i < verificationResult.Errors.Count; i++) {
10161019
verificationResult.Errors[i] = vcg.extractLoopTrace(verificationResult.Errors[i], impl.Name,
10171020
program, extractLoopMappingInfo);
@@ -1023,13 +1026,13 @@ private VerificationResult VerifyImplementationWithoutCaching(Program program, P
10231026
var errorInfo = errorInformationFactory.CreateErrorInformation(impl.tok,
10241027
String.Format("{0} (encountered in implementation {1}).", e.Message, impl.Name), requestId, "Error");
10251028
errorInfo.ImplementationName = impl.Name;
1026-
printer.WriteErrorInformation(errorInfo, output);
10271029
if (er != null) {
10281030
lock (er) {
10291031
er(errorInfo);
10301032
}
10311033
}
10321034

1035+
verificationResult.ErrorBeforeVerification = errorInfo;
10331036
verificationResult.Errors = null;
10341037
verificationResult.Outcome = VCGen.Outcome.Inconclusive;
10351038
}
@@ -1082,7 +1085,7 @@ private PipelineOutcome RunHoudini(Program program, PipelineStatistics stats, Er
10821085
}
10831086

10841087
Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics();
1085-
Houdini.Houdini houdini = new Houdini.Houdini(Options, program, houdiniStats);
1088+
Houdini.Houdini houdini = new Houdini.Houdini(Console.Out, Options, program, houdiniStats);
10861089
Houdini.HoudiniOutcome outcome = houdini.PerformHoudiniInference();
10871090
houdini.Close();
10881091

@@ -1136,7 +1139,7 @@ public Program ProgramFromFile(string filename)
11361139
private PipelineOutcome RunStagedHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er)
11371140
{
11381141
Houdini.HoudiniSession.HoudiniStatistics houdiniStats = new Houdini.HoudiniSession.HoudiniStatistics();
1139-
var stagedHoudini = new Houdini.StagedHoudini(Options, program, houdiniStats, ProgramFromFile);
1142+
var stagedHoudini = new Houdini.StagedHoudini(Console.Out, Options, program, houdiniStats, ProgramFromFile);
11401143
Houdini.HoudiniOutcome outcome = stagedHoudini.PerformStagedHoudiniInference();
11411144

11421145
if (Options.PrintAssignment)

Source/ExecutionEngine/OutputCollector.cs

Lines changed: 0 additions & 41 deletions
This file was deleted.

Source/ExecutionEngine/VerificationResult.cs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public int ProofObligationCount
3333
public List<Counterexample> Errors;
3434
public List<VCResult> VCResults;
3535

36-
public ISet<byte[]> AssertionChecksums { get; private set; }
36+
public ISet<byte[]> AssertionChecksums { get; }
37+
public ErrorInformation ErrorBeforeVerification { get; set; }
3738

3839
public VerificationResult(string requestId, Implementation implementation, string programId = null)
3940
{
@@ -47,9 +48,13 @@ public VerificationResult(string requestId, Implementation implementation, strin
4748
MessageIfVerifies = implementation.FindStringAttribute("msg_if_verifies");
4849
}
4950

50-
public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, int index,
51-
OutputCollector outputCollector, StringWriter output, Implementation impl, bool wasCached)
51+
public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporterDelegate er, TextWriter output,
52+
Implementation impl, bool wasCached)
5253
{
54+
if (ErrorBeforeVerification != null) {
55+
ExecutionEngine.printer.WriteErrorInformation(ErrorBeforeVerification, output);
56+
}
57+
5358
engine.ProcessOutcome(Outcome, Errors, engine.TimeIndication(this), stats,
5459
output, impl.GetTimeLimit(engine.Options), er, ImplementationName,
5560
ImplementationToken,
@@ -71,13 +76,5 @@ public void Emit(ExecutionEngine engine, PipelineStatistics stats, ErrorReporter
7176
ResourceCount);
7277
}
7378
}
74-
75-
outputCollector.Add(index, output);
76-
77-
outputCollector.WriteMoreOutput();
78-
79-
if (Outcome == VCGen.Outcome.Errors || engine.Options.Trace) {
80-
Console.Out.Flush();
81-
}
8279
}
8380
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.Boogie;
8+
9+
class WriterWrapper : TextWriter {
10+
protected TextWriter target;
11+
public override Encoding Encoding => target.Encoding;
12+
13+
protected WriterWrapper(TextWriter target) {
14+
this.target = target;
15+
}
16+
17+
public override void Write(char value) {
18+
target.Write(value);
19+
}
20+
21+
public override void Write(char[] buffer, int index, int count) {
22+
target.Write(buffer, index, count);
23+
}
24+
25+
public override void Write(ReadOnlySpan<char> buffer) {
26+
target.Write(buffer);
27+
}
28+
29+
public override void Write(string value) {
30+
target.Write(value);
31+
}
32+
33+
public override void WriteLine(char[] buffer, int index, int count) {
34+
target.WriteLine(buffer, index, count);
35+
}
36+
37+
public override void WriteLine(ReadOnlySpan<char> buffer) {
38+
target.WriteLine(buffer);
39+
}
40+
41+
public override void WriteLine(StringBuilder value) {
42+
target.WriteLine(value);
43+
}
44+
45+
public override void Write(StringBuilder value) {
46+
target.Write(value);
47+
}
48+
49+
public override Task WriteAsync(char value) {
50+
return target.WriteAsync(value);
51+
}
52+
53+
public override Task WriteAsync(char[] buffer, int index, int count) {
54+
return target.WriteAsync(buffer, index, count);
55+
}
56+
57+
public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = new()) {
58+
return target.WriteAsync(buffer, cancellationToken);
59+
}
60+
61+
public override Task WriteAsync(string value) {
62+
return target.WriteAsync(value);
63+
}
64+
65+
public override Task WriteAsync(StringBuilder value, CancellationToken cancellationToken = new()) {
66+
return target.WriteAsync(value, cancellationToken);
67+
}
68+
69+
public override Task WriteLineAsync(char value) {
70+
return target.WriteLineAsync(value);
71+
}
72+
73+
public override Task WriteLineAsync(char[] buffer, int index, int count) {
74+
return target.WriteLineAsync(buffer, index, count);
75+
}
76+
77+
public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = new()) {
78+
return target.WriteLineAsync(buffer, cancellationToken);
79+
}
80+
81+
public override Task WriteLineAsync(string value) {
82+
return target.WriteLineAsync(value);
83+
}
84+
85+
public override Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = new()) {
86+
return target.WriteLineAsync(value, cancellationToken);
87+
}
88+
}

Source/Houdini/ConcurrentHoudini.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Concurrent;
44
using System.Diagnostics;
55
using System.Diagnostics.Contracts;
6+
using System.IO;
67
using System.Linq;
78

89
namespace Microsoft.Boogie.Houdini
@@ -18,14 +19,14 @@ public static ConcurrentDictionary<string, RefutedAnnotation> RefutedSharedAnnot
1819
get { return refutedSharedAnnotations; }
1920
}
2021

21-
public ConcurrentHoudini(HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats,
22+
public ConcurrentHoudini(TextWriter traceWriter, HoudiniOptions options, int taskId, Program program, HoudiniSession.HoudiniStatistics stats,
2223
string cexTraceFile = "houdiniCexTrace.txt") : base(options)
2324
{
2425
Contract.Assert(taskId >= 0);
2526
this.program = program;
2627
this.cexTraceFile = cexTraceFile;
2728
this.taskID = taskId;
28-
Initialize(program, stats);
29+
Initialize(traceWriter, program, stats);
2930
}
3031

3132
static ConcurrentHoudini()

Source/Houdini/Houdini.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,15 +416,15 @@ protected Houdini(HoudiniOptions options)
416416
this.Options = options;
417417
}
418418

419-
public Houdini(HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt")
419+
public Houdini(TextWriter traceWriter, HoudiniOptions options, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt")
420420
{
421421
this.Options = options;
422422
this.program = program;
423423
this.cexTraceFile = cexTraceFile;
424-
Initialize(program, stats);
424+
Initialize(traceWriter, program, stats);
425425
}
426426

427-
protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats)
427+
protected void Initialize(TextWriter traceWriter, Program program, HoudiniSession.HoudiniStatistics stats)
428428
{
429429
if (Options.Trace)
430430
{
@@ -490,7 +490,7 @@ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stat
490490
}
491491

492492
HoudiniSession session =
493-
new HoudiniSession(this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID());
493+
new HoudiniSession(traceWriter, this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID());
494494
houdiniSessions.Add(impl, session);
495495
}
496496
catch (VCGenException)

0 commit comments

Comments
 (0)