| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // VMOptions=--old_gen_heap_size=64 --no-background-compilation | |
| library slow_consumer2_test; | |
| import 'dart:async'; | |
| import 'package:expect/async_helper.dart'; | |
| import 'package:expect/expect.dart'; | |
| const int KB = 1024; | |
| const int MB = KB * KB; | |
| const int GB = KB * KB * KB; | |
| class SlowConsumer implements StreamConsumer { | |
| int receivedCount = 0; | |
| final int bytesPerSecond; | |
| final int bufferSize; | |
| final List bufferedData = []; | |
| int usedBufferSize = 0; | |
| late int finalCount; | |
| SlowConsumer(int this.bytesPerSecond, int this.bufferSize); | |
| Future consume(Stream stream) { | |
| return addStream(stream).then((_) => close()); | |
| } | |
| Future addStream(Stream stream) { | |
| Completer result = new Completer(); | |
| var subscription; | |
| subscription = stream.listen( | |
| (dynamic _data) { | |
| List<int> data = _data; | |
| receivedCount += data.length; | |
| usedBufferSize += data.length; | |
| bufferedData.add(data); | |
| int currentBufferedDataLength = bufferedData.length; | |
| if (usedBufferSize > bufferSize) { | |
| subscription.pause(); | |
| usedBufferSize = 0; | |
| int ms = data.length * 1000 ~/ bytesPerSecond; | |
| Duration duration = new Duration(milliseconds: ms); | |
| new Timer(duration, () { | |
| for (int i = 0; i < currentBufferedDataLength; i++) { | |
| bufferedData[i] = null; | |
| } | |
| subscription.resume(); | |
| }); | |
| } | |
| }, | |
| onDone: () { | |
| finalCount = receivedCount; | |
| result.complete(receivedCount); | |
| }, | |
| ); | |
| return result.future; | |
| } | |
| Future close() { | |
| return new Future.value(finalCount); | |
| } | |
| } | |
| class DataProvider { | |
| final int chunkSize; | |
| final int bytesPerSecond; | |
| int sentCount = 0; | |
| int targetCount; | |
| late StreamController controller; | |
| DataProvider(int this.bytesPerSecond, int this.targetCount, this.chunkSize) { | |
| controller = new StreamController( | |
| sync: true, | |
| onPause: onPauseStateChange, | |
| onResume: onPauseStateChange, | |
| ); | |
| Timer.run(send); | |
| } | |
| Stream get stream => controller.stream; | |
| send() { | |
| if (controller.isPaused) return; | |
| if (sentCount == targetCount) { | |
| controller.close(); | |
| return; | |
| } | |
| int listSize = chunkSize; | |
| sentCount += listSize; | |
| if (sentCount > targetCount) { | |
| listSize -= sentCount - targetCount; | |
| sentCount = targetCount; | |
| } | |
| controller.add(List.filled(listSize, -1)); | |
| int ms = listSize * 1000 ~/ bytesPerSecond; | |
| Duration duration = new Duration(milliseconds: ms); | |
| if (!controller.isPaused) new Timer(duration, send); | |
| } | |
| onPauseStateChange() { | |
| // We don't care if we just unpaused or paused. In either case we just | |
| // call send which will test it for us. | |
| send(); | |
| } | |
| } | |
| main() { | |
| asyncStart(); | |
| // The data provider can deliver 800MB/s of data. It sends 100MB of data to | |
| // the slower consumer who can only read 200MB/s. The data is sent in 1MB | |
| // chunks. The consumer has a buffer of 5MB. That is, it can accept a few | |
| // packages without pausing its input. | |
| // | |
| // This test is limited to 32MB of heap-space (see VMOptions on top of the | |
| // file). If the consumer doesn't pause the data-provider it will run out of | |
| // heap-space. | |
| new DataProvider( | |
| 800 * MB, | |
| 100 * MB, | |
| 1 * MB, | |
| ).stream.pipe(new SlowConsumer(200 * MB, 5 * MB)).then((count) { | |
| Expect.equals(100 * MB, count); | |
| asyncEnd(); | |
| }); | |
| } |