public static async Task RunAsyncDynamicReaderVariants(Options opts, Func <IBoundConfiguration <dynamic>, Func <string, TextReader>, Task> run, int expectedRuns = 1) { var defaultConfig = Configuration.ForDynamic(opts); var smallBufferConfig = Configuration.ForDynamic(opts.NewBuilder().WithReadBufferSizeHint(1).Build()); // default buffer { var runCount = 0; await run(defaultConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(expectedRuns, runCount); } // small buffer { var runCount = 0; await run(smallBufferConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(expectedRuns, runCount); } // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakDetectorConfig = Configuration.ForDynamic(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); var runCount = 0; await run(defaultConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(expectedRuns, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } }
internal TrackedMemoryOwner(int id, IMemoryOwner <T> inner, TrackedMemoryPool <T> leaser) { Id = id; Inner = inner; Leaser = leaser; IsDisposed = false; }
public static void RunSyncReaderVariants <T>(Options opts, Action <IBoundConfiguration <T>, Func <string, TextReader> > run) { var defaultConfig = Configuration.For <T>(opts); var smallBufferConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).Build()); // default buffer { var runCount = 0; run(defaultConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); } // small buffer { var runCount = 0; run(smallBufferConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); } // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakDetectorConfig = Configuration.For <T>(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); var runCount = 0; run(defaultConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } }
public MemoryPool <T> GetMemoryPool <T>() { var forType = typeof(T).GetTypeInfo(); if (!MemoryPools.TryGetValue(forType, out var allocated)) { MemoryPools[forType] = allocated = new TrackedMemoryPool <T>(); } return((MemoryPool <T>)allocated); }
public static async Task RunAsyncWriterVariants <T>( Options baseOptions, Func <IBoundConfiguration <T>, Func <TextWriter>, Func <string>, Task> run ) { var defaultConfig = Configuration.For <T>(baseOptions); var noBufferConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).Build()); // sync or async { var runCount = 0; // sync! using (var str = new StringWriter()) { var task = run(defaultConfig, () => str, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; } // sync, no buffer! using (var str = new StringWriter()) { var task = run(noBufferConfig, () => str, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; } // sync, leaks using (var str = new StringWriter()) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).Build()); var task = run(leakConfig, () => str, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; Assert.Equal(0, leakDetector.OutstandingRentals); } // sync, no buffer, leaks using (var str = new StringWriter()) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).WithMemoryPool(leakDetector).Build()); var task = run(leakConfig, () => str, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; Assert.Equal(0, leakDetector.OutstandingRentals); } // async! using (var str = new StringWriter()) using (var slow = new ForcedAsyncWriter(str)) { var task = run(defaultConfig, () => slow, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; } // async, no buffer! using (var str = new StringWriter()) using (var slow = new ForcedAsyncWriter(str)) { var task = run(noBufferConfig, () => slow, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; } // async, leaks using (var str = new StringWriter()) using (var slow = new ForcedAsyncWriter(str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).Build()); var task = run(leakConfig, () => slow, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; Assert.Equal(0, leakDetector.OutstandingRentals); } // async, leaks, no buffer! using (var str = new StringWriter()) using (var slow = new ForcedAsyncWriter(str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).WithMemoryPool(leakDetector).Build()); var task = run(leakConfig, () => slow, () => { runCount++; str.Flush(); str.Close(); return(str.ToString()); }); await task; Assert.Equal(0, leakDetector.OutstandingRentals); } Assert.Equal(8, runCount); } // async, default buffer { // figure out how many chances we have to go async in this test int numAsyncCalls; using (var str = new StringWriter()) using (var writer = new AsyncCounterWriter(str)) { var task = run(defaultConfig, () => writer, () => { str.Flush(); str.Close(); return(str.ToString()); }); await task; numAsyncCalls = writer.Count; } var runAllCombos = numAsyncCalls <= MAX_TO_TEST_EXHAUSTIVELY; if (runAllCombos) { // how many different ways could this flow, sync vs async? var combos = EnumerateAsynchoronousCompletionOptions(numAsyncCalls); foreach (var combo in combos) { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); } // leaks { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).Build()); var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } else { // too many combos to reasonably try them all, but lets at least try all the different change over points for (var i = 0; i < numAsyncCalls; i++) { var combo = new bool[numAsyncCalls]; combo[i] = true; using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); } // leaks { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).Build()); var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } } // async, no buffer { // figure out how many chances we have to go async in this test int numAsyncCalls; using (var str = new StringWriter()) using (var writer = new AsyncCounterWriter(str)) { var task = run(noBufferConfig, () => writer, () => { str.Flush(); str.Close(); return(str.ToString()); }); await task; numAsyncCalls = writer.Count; } var runAllCombos = numAsyncCalls <= MAX_TO_TEST_EXHAUSTIVELY; if (runAllCombos) { // how many different ways could this flow, sync vs async? var combos = EnumerateAsynchoronousCompletionOptions(numAsyncCalls); foreach (var combo in combos) { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var didRun = false; await run(noBufferConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); } // leaks { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).WithMemoryPool(leakDetector).Build()); var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } else { // too many combos to reasonably try them all, but lets at least try all the different change over points for (var i = 0; i < numAsyncCalls; i++) { var combo = new bool[numAsyncCalls]; combo[i] = true; using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var didRun = false; await run(noBufferConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); } // leaks { using (var str = new StringWriter()) using (var writer = new ConfigurableSyncAsyncWriter(combo, str)) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).WithMemoryPool(leakDetector).Build()); var didRun = false; await run(defaultConfig, () => writer, () => { didRun = true; str.Flush(); str.Close(); return(str.ToString()); }); Assert.True(didRun); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } } }
public static void RunSyncWriterVariants <T>( Options baseOptions, Action <IBoundConfiguration <T>, Func <TextWriter>, Func <string> > run ) { var defaultConfig = Configuration.For <T>(baseOptions); var noBufferConfig = Configuration.For <T>(baseOptions.NewBuilder().WithWriteBufferSizeHint(0).Build()); // default using (var writer = new StringWriter()) { var gotWriter = 0; var gotString = 0; run(defaultConfig, () => { gotWriter++; return(writer); }, () => { gotString++; return(writer.ToString()); }); Assert.Equal(1, gotWriter); Assert.Equal(1, gotString); } // no buffer using (var writer = new StringWriter()) { var gotWriter = 0; var gotString = 0; run(noBufferConfig, () => { gotWriter++; return(writer); }, () => { gotString++; return(writer.ToString()); }); Assert.Equal(1, gotWriter); Assert.Equal(1, gotString); } // default, leaks using (var writer = new StringWriter()) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).Build()); var gotWriter = 0; var gotString = 0; run(leakConfig, () => { gotWriter++; return(writer); }, () => { gotString++; return(writer.ToString()); }); Assert.Equal(1, gotWriter); Assert.Equal(1, gotString); Assert.Equal(0, leakDetector.OutstandingRentals); } // no buffer, leaks using (var writer = new StringWriter()) { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(baseOptions.NewBuilder().WithMemoryPool(leakDetector).WithWriteBufferSizeHint(0).Build()); var gotWriter = 0; var gotString = 0; run(leakConfig, () => { gotWriter++; return(writer); }, () => { gotString++; return(writer.ToString()); }); Assert.Equal(1, gotWriter); Assert.Equal(1, gotString); // you'd think we'd expect NO rentals, but there are cases where we _have_ to buffer // so just deal with it Assert.Equal(0, leakDetector.OutstandingRentals); } }
public static async Task RunAsyncReaderVariants <T>(Options opts, Func <IBoundConfiguration <T>, Func <string, TextReader>, Task> run) { var defaultConfig = Configuration.For <T>(opts); var smallBufferConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).Build()); // default buffer { // probably sync { var runCount = 0; await run(defaultConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); runCount = 0; await run(leakConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } // async { var runCount = 0; await run(defaultConfig, str => { runCount++; return(new ForcedAsyncReader(new StringReader(str))); }); Assert.Equal(1, runCount); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); runCount = 0; await run(leakConfig, str => { runCount++; return(new ForcedAsyncReader(new StringReader(str))); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } // figure out how many chances we have to go async in this test AsyncCounterReader reader = null; await run(defaultConfig, str => { return(reader ??= new AsyncCounterReader(new StringReader(str))); }); var numAsyncCalls = reader.Count; var runAllCombos = numAsyncCalls <= MAX_TO_TEST_EXHAUSTIVELY; if (runAllCombos) { // how many different ways could this flow, sync vs async? var combos = EnumerateAsynchoronousCompletionOptions(numAsyncCalls); foreach (var combo in combos) { var didRun = false; await run(defaultConfig, str => { didRun = true; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.True(didRun); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); var runCount = 0; await run(leakConfig, str => { runCount++; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } } else { for (var i = 0; i < numAsyncCalls; i++) { var combo = new bool[numAsyncCalls]; combo[i] = true; var didRun = false; await run(defaultConfig, str => { didRun = true; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.True(didRun); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithMemoryPool(leakDetector).Build()); var runCount = 0; await run(leakConfig, str => { runCount++; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } // very small buffer { // probably sync { var runCount = 0; await run(smallBufferConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).WithMemoryPool(leakDetector).Build()); runCount = 0; await run(leakConfig, str => { runCount++; return(new StringReader(str)); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } // async { var runCount = 0; await run(smallBufferConfig, str => { runCount++; return(new ForcedAsyncReader(new StringReader(str))); }); Assert.Equal(1, runCount); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).WithMemoryPool(leakDetector).Build()); runCount = 0; await run(leakConfig, str => { runCount++; return(new ForcedAsyncReader(new StringReader(str))); }); Assert.Equal(1, runCount); } } // figure out how many chances we have to go async in this test AsyncCounterReader reader = null; await run(smallBufferConfig, str => { return(reader ??= new AsyncCounterReader(new StringReader(str))); }); var numAsyncCalls = reader.Count; var runAllCombos = numAsyncCalls <= MAX_TO_TEST_EXHAUSTIVELY; if (runAllCombos) { // how many different ways could this flow, sync vs async? var combos = EnumerateAsynchoronousCompletionOptions(numAsyncCalls); foreach (var combo in combos) { var didRun = false; await run(smallBufferConfig, str => { didRun = true; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.True(didRun); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).WithMemoryPool(leakDetector).Build()); var runCount = 0; await run(leakConfig, str => { runCount++; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } } else { for (var i = 0; i < numAsyncCalls; i++) { var combo = new bool[numAsyncCalls]; combo[i] = true; var didRun = false; await run(defaultConfig, str => { didRun = true; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.True(didRun); // leaks { var leakDetector = new TrackedMemoryPool <char>(); var leakConfig = Configuration.For <T>(opts.NewBuilder().WithReadBufferSizeHint(1).WithMemoryPool(leakDetector).Build()); var runCount = 0; await run(leakConfig, str => { runCount++; return(new ConfigurableSyncAsyncReader(combo, new StringReader(str))); }); Assert.Equal(1, runCount); Assert.Equal(0, leakDetector.OutstandingRentals); } } } } }