From 12c4f8964265ca665af38f698ffdf4aead6d27a7 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 9 Sep 2019 14:54:12 -0700 Subject: [PATCH 01/46] Added channel buffers to ui so that messages have a place to go until message handling is setup. --- lib/ui/channel_buffers.dart | 48 ++++++++++++++++++++++++++ lib/ui/dart_ui.gni | 1 + lib/ui/hooks.dart | 4 ++- lib/ui/ui.dart | 1 + testing/dart/channel_buffers_test.dart | 39 +++++++++++++++++++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 lib/ui/channel_buffers.dart create mode 100644 testing/dart/channel_buffers_test.dart diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart new file mode 100644 index 0000000000000..c5fe4c34f1f2e --- /dev/null +++ b/lib/ui/channel_buffers.dart @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of dart.ui; + +class StoredMessage { + final ByteData _data; + final PlatformMessageResponseCallback _callback; + + StoredMessage(this._data, this._callback); + + ByteData get data => _data; + + PlatformMessageResponseCallback get callback => _callback; +} + +/// Storage of channel messages until the channels are completely routed. +class ChannelBuffers { + static const int DEFAULT_BUFFER_SIZE = 100; + + // TODO(engine): Convert queue to a ring buffer. + final Map> _messages = {}; + + void push(String channel, ByteData data, PlatformMessageResponseCallback callback) { + collection.Queue queue = _messages[channel]; + if (queue == null) { + queue = collection.Queue(); + _messages[channel] = queue; + } + queue.addLast(StoredMessage(data, callback)); + } + + StoredMessage pop(String channel) { + final collection.Queue queue = _messages[channel]; + return queue?.removeFirst(); + } + + bool isEmpty(String channel) { + final collection.Queue queue = _messages[channel]; + return queue?.isEmpty ?? true; + } + + void resize(String channel, int newSize) { + } +} + +final ChannelBuffers channelBuffers = ChannelBuffers(); diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index c52a9f025f231..3a07fbd526048 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -3,6 +3,7 @@ # found in the LICENSE file. dart_ui_files = [ + "$flutter_root/lib/ui/channel_buffers.dart", "$flutter_root/lib/ui/compositing.dart", "$flutter_root/lib/ui/geometry.dart", "$flutter_root/lib/ui/hash_codes.dart", diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 9c596c2e26008..6fb6b3a1556e1 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -161,7 +161,9 @@ void _dispatchPlatformMessage(String name, ByteData data, int responseId) { }, ); } else { - window._respondToPlatformMessage(responseId, null); + channelBuffers.push(name, data, (ByteData responseData) { + window._respondToPlatformMessage(responseId, responseData); + }); } } diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index cef4db81cee0e..5c04721e7fec0 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -22,6 +22,7 @@ import 'dart:math' as math; import 'dart:nativewrappers'; import 'dart:typed_data'; +part 'channel_buffers.dart'; part 'compositing.dart'; part 'geometry.dart'; part 'hash_codes.dart'; diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart new file mode 100644 index 0000000000000..fbf1929dc6f49 --- /dev/null +++ b/testing/dart/channel_buffers_test.dart @@ -0,0 +1,39 @@ +import 'dart:ui' as ui; +import 'dart:typed_data'; +import 'dart:convert'; + +import 'package:test/test.dart'; + +void main() { + + test('push pop', () async { + String channel = "foo"; + var list = utf8.encode('bar'); + var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; + ByteData data = ByteData.view(buffer); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; + buffers.push(channel, data, callback); + ui.StoredMessage storedMessage = buffers.pop(channel); + expect(storedMessage.data, equals(data)); + expect(storedMessage.callback, equals(callback)); + }); + + test('empty', () async { + String channel = "foo"; + var list = utf8.encode('bar'); + var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; + ByteData data = ByteData.view(buffer); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; + expect(buffers.isEmpty(channel), equals(true)); + buffers.push(channel, data, callback); + expect(buffers.isEmpty(channel), equals(false)); + }); + + test('pop', () async { + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + expect(buffers.pop("channel"), equals(null)); + }); + +} \ No newline at end of file From 8a8343dcffad4db26e84bea69b11ef12b2404789 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 9 Sep 2019 16:04:44 -0700 Subject: [PATCH 02/46] Added the RingBuffer class. --- lib/ui/channel_buffers.dart | 28 +++++++++++++++++++ testing/dart/channel_buffers_test.dart | 26 +++++++++++++++++ .../dart/window_hooks_integration_test.dart | 1 + 3 files changed, 55 insertions(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index c5fe4c34f1f2e..fa78eac0b005b 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -15,6 +15,34 @@ class StoredMessage { PlatformMessageResponseCallback get callback => _callback; } +/// A fixed-size circular queue. +class RingBuffer { + final collection.ListQueue _queue; + final int _capacity; + + RingBuffer(this._capacity) + : _queue = collection.ListQueue(_capacity); + + int get length => _queue.length; + + int get capacity => _capacity; + + /// Returns true on overflow. + bool push(T val) { + bool overflow = false; + while (_queue.length >= _capacity) { + _queue.removeFirst(); + overflow = true; + } + _queue.addLast(val); + return overflow; + } + + T pop() { + return _queue.removeFirst(); + } +} + /// Storage of channel messages until the channels are completely routed. class ChannelBuffers { static const int DEFAULT_BUFFER_SIZE = 100; diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index fbf1929dc6f49..dba7fc4c26975 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -36,4 +36,30 @@ void main() { expect(buffers.pop("channel"), equals(null)); }); + test('ringbuffer push pop', () async { + ui.RingBuffer ringBuffer = ui.RingBuffer(10); + ringBuffer.push(1); + expect(ringBuffer.pop(), equals(1)); + }); + + test('ringbuffer overflow', () async { + ui.RingBuffer ringBuffer = ui.RingBuffer(3); + expect(ringBuffer.push(1), equals(false)); + expect(ringBuffer.push(2), equals(false)); + expect(ringBuffer.push(3), equals(false)); + expect(ringBuffer.push(4), equals(true)); + expect(ringBuffer.pop(), equals(2)); + }); + + test('ringbuffer length', () async { + ui.RingBuffer ringBuffer = ui.RingBuffer(3); + expect(ringBuffer.length, equals(0)); + ringBuffer.push(123); + expect(ringBuffer.length, equals(1)); + }); + + test('ringbuffer underflow', () async { + ui.RingBuffer ringBuffer = ui.RingBuffer(3); + expect(() => ringBuffer.pop(), throwsA(TypeMatcher())); + }); } \ No newline at end of file diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 22ee978fc3cc3..51089abff2375 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -18,6 +18,7 @@ import 'dart:typed_data'; import 'package:test/test.dart'; // HACK: these parts are to get access to private functions tested here. +part '../../lib/ui/channel_buffers.dart'; part '../../lib/ui/compositing.dart'; part '../../lib/ui/geometry.dart'; part '../../lib/ui/hash_codes.dart'; From cff180e0ca084727f54102b0a58e5ef342825dc6 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 9 Sep 2019 17:06:41 -0700 Subject: [PATCH 03/46] Moved over to using the ring buffer. --- lib/ui/channel_buffers.dart | 25 ++++++++++++++++--------- testing/dart/channel_buffers_test.dart | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index fa78eac0b005b..18f65e4dd6019 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -27,6 +27,8 @@ class RingBuffer { int get capacity => _capacity; + bool get isEmpty => _queue.isEmpty; + /// Returns true on overflow. bool push(T val) { bool overflow = false; @@ -39,7 +41,7 @@ class RingBuffer { } T pop() { - return _queue.removeFirst(); + return _queue.isEmpty ? null : _queue.removeFirst(); } } @@ -47,25 +49,30 @@ class RingBuffer { class ChannelBuffers { static const int DEFAULT_BUFFER_SIZE = 100; - // TODO(engine): Convert queue to a ring buffer. - final Map> _messages = {}; + final Map> _messages = {}; void push(String channel, ByteData data, PlatformMessageResponseCallback callback) { - collection.Queue queue = _messages[channel]; + RingBuffer queue = _messages[channel]; if (queue == null) { - queue = collection.Queue(); + queue = RingBuffer(DEFAULT_BUFFER_SIZE); _messages[channel] = queue; } - queue.addLast(StoredMessage(data, callback)); + if (queue.push(StoredMessage(data, callback))) { + _Logger._printString('Overflow on channel:' + channel); + } } StoredMessage pop(String channel) { - final collection.Queue queue = _messages[channel]; - return queue?.removeFirst(); + final RingBuffer queue = _messages[channel]; + final StoredMessage result = queue?.pop(); + if (result == null) { + _Logger._printString('Underflow on channel:' + channel); + } + return result; } bool isEmpty(String channel) { - final collection.Queue queue = _messages[channel]; + final RingBuffer queue = _messages[channel]; return queue?.isEmpty ?? true; } diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index dba7fc4c26975..8277ed4ebe074 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -60,6 +60,6 @@ void main() { test('ringbuffer underflow', () async { ui.RingBuffer ringBuffer = ui.RingBuffer(3); - expect(() => ringBuffer.pop(), throwsA(TypeMatcher())); + expect(ringBuffer.pop(), equals(null)); }); } \ No newline at end of file From 31a845b83a3219618a430a21dd004c2d827ba9cd Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 09:29:10 -0700 Subject: [PATCH 04/46] Did a bit of cleanup. --- lib/ui/channel_buffers.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 18f65e4dd6019..559dcfdab8d18 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -4,14 +4,13 @@ part of dart.ui; +/// A saved platform message for a channel with its callback. class StoredMessage { final ByteData _data; final PlatformMessageResponseCallback _callback; StoredMessage(this._data, this._callback); - ByteData get data => _data; - PlatformMessageResponseCallback get callback => _callback; } @@ -40,6 +39,7 @@ class RingBuffer { return overflow; } + /// Returns null when empty. T pop() { return _queue.isEmpty ? null : _queue.removeFirst(); } From c28999d0712ac1e5e375bac854a1a386e8913021 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 09:58:45 -0700 Subject: [PATCH 05/46] Removed trailing space. --- testing/dart/channel_buffers_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index 8277ed4ebe074..f46ee9a51c2e6 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -62,4 +62,4 @@ void main() { ui.RingBuffer ringBuffer = ui.RingBuffer(3); expect(ringBuffer.pop(), equals(null)); }); -} \ No newline at end of file +} From 4253c1cc61045749c6b830b7025e9b5a3dee072b Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 12:24:35 -0700 Subject: [PATCH 06/46] Made RingBuffer private. --- lib/ui/channel_buffers.dart | 45 +++++++++++++++----- testing/dart/channel_buffers_test.dart | 59 ++++++++++++-------------- 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 559dcfdab8d18..a845dfe1b2cb6 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -15,11 +15,11 @@ class StoredMessage { } /// A fixed-size circular queue. -class RingBuffer { +class _RingBuffer { final collection.ListQueue _queue; - final int _capacity; + int _capacity; - RingBuffer(this._capacity) + _RingBuffer(this._capacity) : _queue = collection.ListQueue(_capacity); int get length => _queue.length; @@ -43,27 +43,45 @@ class RingBuffer { T pop() { return _queue.isEmpty ? null : _queue.removeFirst(); } + + /// Returns the number of discarded items resulting from resize. + int resize(int newSize) { + int result = 0; + + while (length > newSize) { + result += 1; + _queue.removeFirst(); + } + + _capacity = newSize; + + return result; + } } /// Storage of channel messages until the channels are completely routed. class ChannelBuffers { static const int DEFAULT_BUFFER_SIZE = 100; - final Map> _messages = {}; + final Map> _messages = {}; - void push(String channel, ByteData data, PlatformMessageResponseCallback callback) { - RingBuffer queue = _messages[channel]; + /// Returns true on overflow. + bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { + _RingBuffer queue = _messages[channel]; if (queue == null) { - queue = RingBuffer(DEFAULT_BUFFER_SIZE); + queue = _RingBuffer(DEFAULT_BUFFER_SIZE); _messages[channel] = queue; } - if (queue.push(StoredMessage(data, callback))) { + final bool result = queue.push(StoredMessage(data, callback)); + if (result) { _Logger._printString('Overflow on channel:' + channel); } + return result; } + /// Returns null on underflow. StoredMessage pop(String channel) { - final RingBuffer queue = _messages[channel]; + final _RingBuffer queue = _messages[channel]; final StoredMessage result = queue?.pop(); if (result == null) { _Logger._printString('Underflow on channel:' + channel); @@ -72,11 +90,18 @@ class ChannelBuffers { } bool isEmpty(String channel) { - final RingBuffer queue = _messages[channel]; + final _RingBuffer queue = _messages[channel]; return queue?.isEmpty ?? true; } void resize(String channel, int newSize) { + _RingBuffer queue = _messages[channel]; + if (queue == null) { + queue = _RingBuffer(newSize); + _messages[channel] = queue; + } else { + queue.resize(newSize); + } } } diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index f46ee9a51c2e6..d595281a13222 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -6,11 +6,21 @@ import 'package:test/test.dart'; void main() { + ByteData _makeByteData(String str) { + var list = utf8.encode(str); + var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; + return ByteData.view(buffer); + } + + String _getString(ByteData data) { + final buffer = data.buffer; + var list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + return utf8.decode(list); + } + test('push pop', () async { String channel = "foo"; - var list = utf8.encode('bar'); - var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; - ByteData data = ByteData.view(buffer); + ByteData data = _makeByteData('bar'); ui.ChannelBuffers buffers = ui.ChannelBuffers(); ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; buffers.push(channel, data, callback); @@ -21,9 +31,7 @@ void main() { test('empty', () async { String channel = "foo"; - var list = utf8.encode('bar'); - var buffer = list is Uint8List ? list.buffer : new Uint8List.fromList(list).buffer; - ByteData data = ByteData.view(buffer); + ByteData data = _makeByteData('bar'); ui.ChannelBuffers buffers = ui.ChannelBuffers(); ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; expect(buffers.isEmpty(channel), equals(true)); @@ -36,30 +44,19 @@ void main() { expect(buffers.pop("channel"), equals(null)); }); - test('ringbuffer push pop', () async { - ui.RingBuffer ringBuffer = ui.RingBuffer(10); - ringBuffer.push(1); - expect(ringBuffer.pop(), equals(1)); - }); - - test('ringbuffer overflow', () async { - ui.RingBuffer ringBuffer = ui.RingBuffer(3); - expect(ringBuffer.push(1), equals(false)); - expect(ringBuffer.push(2), equals(false)); - expect(ringBuffer.push(3), equals(false)); - expect(ringBuffer.push(4), equals(true)); - expect(ringBuffer.pop(), equals(2)); - }); - - test('ringbuffer length', () async { - ui.RingBuffer ringBuffer = ui.RingBuffer(3); - expect(ringBuffer.length, equals(0)); - ringBuffer.push(123); - expect(ringBuffer.length, equals(1)); - }); - - test('ringbuffer underflow', () async { - ui.RingBuffer ringBuffer = ui.RingBuffer(3); - expect(ringBuffer.pop(), equals(null)); + test('overflow', () async { + String channel = "foo"; + ByteData one = _makeByteData('one'); + ByteData two = _makeByteData('two'); + ByteData three = _makeByteData('three'); + ByteData four = _makeByteData('four'); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; + buffers.resize(channel, 3); + expect(buffers.push(channel, one, callback), equals(false)); + expect(buffers.push(channel, two, callback), equals(false)); + expect(buffers.push(channel, three, callback), equals(false)); + expect(buffers.push(channel, four, callback), equals(true)); + expect(_getString(buffers.pop(channel).data), equals('two')); }); } From e19f59c6af1ef3d262b7bd7b94f704c803530925 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 12:32:20 -0700 Subject: [PATCH 07/46] Fixed formatting and documenation on default ringbuffer size. --- lib/ui/channel_buffers.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a845dfe1b2cb6..65245b0d1c208 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -61,7 +61,9 @@ class _RingBuffer { /// Storage of channel messages until the channels are completely routed. class ChannelBuffers { - static const int DEFAULT_BUFFER_SIZE = 100; + /// A somewhat arbitrary size that tries to balance handling typical + /// cases and not wasting memory. + static const int kDefaultBufferSize = 100; final Map> _messages = {}; @@ -69,7 +71,7 @@ class ChannelBuffers { bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { _RingBuffer queue = _messages[channel]; if (queue == null) { - queue = _RingBuffer(DEFAULT_BUFFER_SIZE); + queue = _RingBuffer(kDefaultBufferSize); _messages[channel] = queue; } final bool result = queue.push(StoredMessage(data, callback)); From 63f7b8a48f6761b89464ac87693453e3c19c9ea6 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 12:43:24 -0700 Subject: [PATCH 08/46] Updated the warning messages. --- lib/ui/channel_buffers.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 65245b0d1c208..f5fdebcea5ed3 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -76,7 +76,11 @@ class ChannelBuffers { } final bool result = queue.push(StoredMessage(data, callback)); if (result) { - _Logger._printString('Overflow on channel:' + channel); + _Logger._printString('Overflow on channel: $channel. ' + 'Messages on this channel are being sent faster ' + 'than they are being processed which is resulting ' + 'in the dropping of messages. The engine may not be ' + 'running or you need to adjust the buffer size.'); } return result; } @@ -85,9 +89,6 @@ class ChannelBuffers { StoredMessage pop(String channel) { final _RingBuffer queue = _messages[channel]; final StoredMessage result = queue?.pop(); - if (result == null) { - _Logger._printString('Underflow on channel:' + channel); - } return result; } From 4e87cb0c20a84a8d0b80abf7e7df38ef48fbecc2 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 12:53:31 -0700 Subject: [PATCH 09/46] Expanded on docstring. --- lib/ui/channel_buffers.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index f5fdebcea5ed3..bf12a89273278 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -60,6 +60,11 @@ class _RingBuffer { } /// Storage of channel messages until the channels are completely routed. +/// +/// Each channel has a finite buffer capacity and in a FIFO manner messages will +/// be deleted if the capacity is exceeded. The intention is that these buffers +/// will be drained once a callback is setup on the BinaryMessenger in the +/// Flutter framework. class ChannelBuffers { /// A somewhat arbitrary size that tries to balance handling typical /// cases and not wasting memory. From 24607abf41766cfc372ebfe8639466cdae92e695 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 13:09:15 -0700 Subject: [PATCH 10/46] Updated constructor location and docstring. --- lib/ui/channel_buffers.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index bf12a89273278..e2e080a4dee98 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -6,10 +6,15 @@ part of dart.ui; /// A saved platform message for a channel with its callback. class StoredMessage { + /// Default constructor, takes in a [ByteData] that represents the + /// payload of the message and a [PlatformMessageResponseCallback] + /// that represents the callback that will be called when the message + /// is handled. + StoredMessage(this._data, this._callback); + final ByteData _data; final PlatformMessageResponseCallback _callback; - StoredMessage(this._data, this._callback); ByteData get data => _data; PlatformMessageResponseCallback get callback => _callback; } From c4bec51abef236293a5963f0fa71e54b847bbffb Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 13:22:35 -0700 Subject: [PATCH 11/46] Added extra test for resizing. --- lib/ui/channel_buffers.dart | 5 ++++- testing/dart/channel_buffers_test.dart | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index e2e080a4dee98..9863214b2a43f 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -113,7 +113,10 @@ class ChannelBuffers { queue = _RingBuffer(newSize); _messages[channel] = queue; } else { - queue.resize(newSize); + final int numberOfDroppedMessages = queue.resize(newSize); + if (numberOfDroppedMessages > 0) { + _Logger._printString('Dropping messages on channel: $channel as a result of shrinking the buffer size.'); + } } } } diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index d595281a13222..590e00f7cfb53 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -59,4 +59,16 @@ void main() { expect(buffers.push(channel, four, callback), equals(true)); expect(_getString(buffers.pop(channel).data), equals('two')); }); + + test('resize drop', () async { + String channel = "foo"; + ByteData one = _makeByteData('one'); + ByteData two = _makeByteData('two'); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; + expect(buffers.push(channel, one, callback), equals(false)); + expect(buffers.push(channel, two, callback), equals(false)); + buffers.resize(channel, 1); + expect(_getString(buffers.pop(channel).data), equals('two')); + }); } From ea3823f7dc4b068edf074723b7345003b4cedcb8 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 13:28:09 -0700 Subject: [PATCH 12/46] Switched to ternary operator since it's more clear. --- lib/ui/channel_buffers.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 9863214b2a43f..d0beb272e2a6b 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -104,7 +104,7 @@ class ChannelBuffers { bool isEmpty(String channel) { final _RingBuffer queue = _messages[channel]; - return queue?.isEmpty ?? true; + return (queue == null) ? true : queue.isEmpty; } void resize(String channel, int newSize) { @@ -115,7 +115,7 @@ class ChannelBuffers { } else { final int numberOfDroppedMessages = queue.resize(newSize); if (numberOfDroppedMessages > 0) { - _Logger._printString('Dropping messages on channel: $channel as a result of shrinking the buffer size.'); + _Logger._printString('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); } } } From afc09b69b0c782c739b248aed8618c9864e885f2 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 13:39:57 -0700 Subject: [PATCH 13/46] added type annotation to literal --- lib/ui/channel_buffers.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index d0beb272e2a6b..a5fc80139f6d2 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -75,7 +75,8 @@ class ChannelBuffers { /// cases and not wasting memory. static const int kDefaultBufferSize = 100; - final Map> _messages = {}; + final Map> _messages = + >{}; /// Returns true on overflow. bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { From 0a3c609b85593844697de50a47e89e6968556182 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 13:52:51 -0700 Subject: [PATCH 14/46] added new file to licenses_flutter --- ci/licenses_golden/licenses_flutter | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8e21402b2bb5b..fd99552344233 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -230,6 +230,7 @@ FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h FILE: ../../../flutter/lib/snapshot/libraries.json FILE: ../../../flutter/lib/snapshot/snapshot.h +FILE: ../../../flutter/lib/ui/channel_buffers.dart FILE: ../../../flutter/lib/ui/compositing.dart FILE: ../../../flutter/lib/ui/compositing/scene.cc FILE: ../../../flutter/lib/ui/compositing/scene.h From 9df8b239375c3b3c0f3069f7e745aff055aa95d5 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:02:16 -0700 Subject: [PATCH 15/46] added channel buffers to the web ui.dart --- lib/web_ui/lib/ui.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index 9ea3e1fcedcec..cb71d24c1d01f 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -23,6 +23,7 @@ export 'src/engine.dart' webOnlyInitializeEngine; part 'src/ui/canvas.dart'; +part 'src/ui/channel_buffers.dart'; part 'src/ui/compositing.dart'; part 'src/ui/geometry.dart'; part 'src/ui/hash_codes.dart'; From f32af48c1ca9aeb4e456a474191fb6c1a34f5bcf Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:20:57 -0700 Subject: [PATCH 16/46] added channel_buffers to web_ui --- lib/web_ui/lib/src/ui/channel_buffers.dart | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 lib/web_ui/lib/src/ui/channel_buffers.dart diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart new file mode 100644 index 0000000000000..a5fc80139f6d2 --- /dev/null +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of dart.ui; + +/// A saved platform message for a channel with its callback. +class StoredMessage { + /// Default constructor, takes in a [ByteData] that represents the + /// payload of the message and a [PlatformMessageResponseCallback] + /// that represents the callback that will be called when the message + /// is handled. + StoredMessage(this._data, this._callback); + + final ByteData _data; + final PlatformMessageResponseCallback _callback; + + ByteData get data => _data; + PlatformMessageResponseCallback get callback => _callback; +} + +/// A fixed-size circular queue. +class _RingBuffer { + final collection.ListQueue _queue; + int _capacity; + + _RingBuffer(this._capacity) + : _queue = collection.ListQueue(_capacity); + + int get length => _queue.length; + + int get capacity => _capacity; + + bool get isEmpty => _queue.isEmpty; + + /// Returns true on overflow. + bool push(T val) { + bool overflow = false; + while (_queue.length >= _capacity) { + _queue.removeFirst(); + overflow = true; + } + _queue.addLast(val); + return overflow; + } + + /// Returns null when empty. + T pop() { + return _queue.isEmpty ? null : _queue.removeFirst(); + } + + /// Returns the number of discarded items resulting from resize. + int resize(int newSize) { + int result = 0; + + while (length > newSize) { + result += 1; + _queue.removeFirst(); + } + + _capacity = newSize; + + return result; + } +} + +/// Storage of channel messages until the channels are completely routed. +/// +/// Each channel has a finite buffer capacity and in a FIFO manner messages will +/// be deleted if the capacity is exceeded. The intention is that these buffers +/// will be drained once a callback is setup on the BinaryMessenger in the +/// Flutter framework. +class ChannelBuffers { + /// A somewhat arbitrary size that tries to balance handling typical + /// cases and not wasting memory. + static const int kDefaultBufferSize = 100; + + final Map> _messages = + >{}; + + /// Returns true on overflow. + bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { + _RingBuffer queue = _messages[channel]; + if (queue == null) { + queue = _RingBuffer(kDefaultBufferSize); + _messages[channel] = queue; + } + final bool result = queue.push(StoredMessage(data, callback)); + if (result) { + _Logger._printString('Overflow on channel: $channel. ' + 'Messages on this channel are being sent faster ' + 'than they are being processed which is resulting ' + 'in the dropping of messages. The engine may not be ' + 'running or you need to adjust the buffer size.'); + } + return result; + } + + /// Returns null on underflow. + StoredMessage pop(String channel) { + final _RingBuffer queue = _messages[channel]; + final StoredMessage result = queue?.pop(); + return result; + } + + bool isEmpty(String channel) { + final _RingBuffer queue = _messages[channel]; + return (queue == null) ? true : queue.isEmpty; + } + + void resize(String channel, int newSize) { + _RingBuffer queue = _messages[channel]; + if (queue == null) { + queue = _RingBuffer(newSize); + _messages[channel] = queue; + } else { + final int numberOfDroppedMessages = queue.resize(newSize); + if (numberOfDroppedMessages > 0) { + _Logger._printString('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); + } + } + } +} + +final ChannelBuffers channelBuffers = ChannelBuffers(); From 97be8ca55d9b26b3ffa132612d1f28596f743bbb Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:30:57 -0700 Subject: [PATCH 17/46] For web_ui, replaced ListQueue with Queue and removed log statements. --- lib/web_ui/lib/src/ui/channel_buffers.dart | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index a5fc80139f6d2..4d9c14d4f849d 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -21,11 +21,11 @@ class StoredMessage { /// A fixed-size circular queue. class _RingBuffer { - final collection.ListQueue _queue; + final collection.Queue _queue; int _capacity; _RingBuffer(this._capacity) - : _queue = collection.ListQueue(_capacity); + : _queue = collection.Queue(); int get length => _queue.length; @@ -85,15 +85,7 @@ class ChannelBuffers { queue = _RingBuffer(kDefaultBufferSize); _messages[channel] = queue; } - final bool result = queue.push(StoredMessage(data, callback)); - if (result) { - _Logger._printString('Overflow on channel: $channel. ' - 'Messages on this channel are being sent faster ' - 'than they are being processed which is resulting ' - 'in the dropping of messages. The engine may not be ' - 'running or you need to adjust the buffer size.'); - } - return result; + return queue.push(StoredMessage(data, callback)); } /// Returns null on underflow. @@ -114,10 +106,7 @@ class ChannelBuffers { queue = _RingBuffer(newSize); _messages[channel] = queue; } else { - final int numberOfDroppedMessages = queue.resize(newSize); - if (numberOfDroppedMessages > 0) { - _Logger._printString('Dropping messages on channel "$channel" as a result of shrinking the buffer size.'); - } + queue.resize(newSize); } } } From d041d62262f39d404ec48c8299ad46e4523dbbe4 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:32:44 -0700 Subject: [PATCH 18/46] fixed collections reference --- lib/web_ui/lib/src/ui/channel_buffers.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index 4d9c14d4f849d..5852e508de4fe 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -21,11 +21,11 @@ class StoredMessage { /// A fixed-size circular queue. class _RingBuffer { - final collection.Queue _queue; + final ListQueue _queue; int _capacity; _RingBuffer(this._capacity) - : _queue = collection.Queue(); + : _queue = ListQueue(_capacity); int get length => _queue.length; From 15334284ba476239b01f81ad30c0df1803fc5d06 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:41:25 -0700 Subject: [PATCH 19/46] Added web_ui file to licenses_flutter --- ci/licenses_golden/licenses_flutter | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fd99552344233..aad6a09d1ff23 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -429,6 +429,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/validators.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/window.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/canvas.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/channel_buffers.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/geometry.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/hash_codes.dart From 42c742d1b47bc52ec054342b4c9c0fc583319122 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 10 Sep 2019 14:57:06 -0700 Subject: [PATCH 20/46] fixed web_ui "part of" declaration --- lib/web_ui/lib/src/ui/channel_buffers.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index 5852e508de4fe..6f950cfe345af 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -part of dart.ui; +part of ui; /// A saved platform message for a channel with its callback. class StoredMessage { From 652a822b2de8220561578c765331f110ac01f4d9 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 10:39:44 -0700 Subject: [PATCH 21/46] Added docstrings. --- lib/ui/channel_buffers.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a5fc80139f6d2..364510fa7ee1e 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -15,7 +15,9 @@ class StoredMessage { final ByteData _data; final PlatformMessageResponseCallback _callback; + /// Getter for _data field, represents the message's payload. ByteData get data => _data; + /// Getter for the _callback field, to be called when the message is received. PlatformMessageResponseCallback get callback => _callback; } From 4d84d1b8fc3b764e6dc4a8fc79fd7243fce6caec Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 10:42:35 -0700 Subject: [PATCH 22/46] update docstring --- lib/ui/channel_buffers.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 364510fa7ee1e..a0176071e9af2 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -66,7 +66,8 @@ class _RingBuffer { } } -/// Storage of channel messages until the channels are completely routed. +/// Storage of channel messages until the channels are completely routed +/// (ie when a message handler is attached to the channel on the framework side). /// /// Each channel has a finite buffer capacity and in a FIFO manner messages will /// be deleted if the capacity is exceeded. The intention is that these buffers From 33c21e90f23871795668af05e74aaf104739656f Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 11:23:54 -0700 Subject: [PATCH 23/46] Reduced the exposed surface of the mechanism so it's just one function, drainBufferChannel. --- lib/ui/channel_buffers.dart | 41 +++++--- lib/ui/hooks.dart | 2 +- lib/web_ui/lib/src/ui/channel_buffers.dart | 111 ++------------------- 3 files changed, 34 insertions(+), 120 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a0176071e9af2..da3daec8ce05c 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -5,12 +5,12 @@ part of dart.ui; /// A saved platform message for a channel with its callback. -class StoredMessage { +class _StoredMessage { /// Default constructor, takes in a [ByteData] that represents the /// payload of the message and a [PlatformMessageResponseCallback] /// that represents the callback that will be called when the message /// is handled. - StoredMessage(this._data, this._callback); + _StoredMessage(this._data, this._callback); final ByteData _data; final PlatformMessageResponseCallback _callback; @@ -78,17 +78,17 @@ class ChannelBuffers { /// cases and not wasting memory. static const int kDefaultBufferSize = 100; - final Map> _messages = - >{}; + final Map> _messages = + >{}; /// Returns true on overflow. bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { - _RingBuffer queue = _messages[channel]; + _RingBuffer<_StoredMessage> queue = _messages[channel]; if (queue == null) { - queue = _RingBuffer(kDefaultBufferSize); + queue = _RingBuffer<_StoredMessage>(kDefaultBufferSize); _messages[channel] = queue; } - final bool result = queue.push(StoredMessage(data, callback)); + final bool result = queue.push(_StoredMessage(data, callback)); if (result) { _Logger._printString('Overflow on channel: $channel. ' 'Messages on this channel are being sent faster ' @@ -100,21 +100,21 @@ class ChannelBuffers { } /// Returns null on underflow. - StoredMessage pop(String channel) { - final _RingBuffer queue = _messages[channel]; - final StoredMessage result = queue?.pop(); + _StoredMessage pop(String channel) { + final _RingBuffer<_StoredMessage> queue = _messages[channel]; + final _StoredMessage result = queue?.pop(); return result; } bool isEmpty(String channel) { - final _RingBuffer queue = _messages[channel]; + final _RingBuffer<_StoredMessage> queue = _messages[channel]; return (queue == null) ? true : queue.isEmpty; } void resize(String channel, int newSize) { - _RingBuffer queue = _messages[channel]; + _RingBuffer<_StoredMessage> queue = _messages[channel]; if (queue == null) { - queue = _RingBuffer(newSize); + queue = _RingBuffer<_StoredMessage>(newSize); _messages[channel] = queue; } else { final int numberOfDroppedMessages = queue.resize(newSize); @@ -125,4 +125,17 @@ class ChannelBuffers { } } -final ChannelBuffers channelBuffers = ChannelBuffers(); +typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); + +/// Remove and process all stored messages for a given channel. +/// +/// This should be called once a channel is prepared to handle messages +/// (ie when a message handler is setup in the framework). +void drainChannelBuffer(String channel, DrainChannelCallback callback) async { + while (!_channelBuffers.isEmpty(channel)) { + final _StoredMessage message = _channelBuffers.pop(channel); + await callback(message.data, message.callback); + } +} + +final ChannelBuffers _channelBuffers = ChannelBuffers(); diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 6fb6b3a1556e1..8e45a22de6c5e 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -161,7 +161,7 @@ void _dispatchPlatformMessage(String name, ByteData data, int responseId) { }, ); } else { - channelBuffers.push(name, data, (ByteData responseData) { + _channelBuffers.push(name, data, (ByteData responseData) { window._respondToPlatformMessage(responseId, responseData); }); } diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index 6f950cfe345af..bcfb97276720e 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -4,111 +4,12 @@ part of ui; -/// A saved platform message for a channel with its callback. -class StoredMessage { - /// Default constructor, takes in a [ByteData] that represents the - /// payload of the message and a [PlatformMessageResponseCallback] - /// that represents the callback that will be called when the message - /// is handled. - StoredMessage(this._data, this._callback); +typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); - final ByteData _data; - final PlatformMessageResponseCallback _callback; - - ByteData get data => _data; - PlatformMessageResponseCallback get callback => _callback; -} - -/// A fixed-size circular queue. -class _RingBuffer { - final ListQueue _queue; - int _capacity; - - _RingBuffer(this._capacity) - : _queue = ListQueue(_capacity); - - int get length => _queue.length; - - int get capacity => _capacity; - - bool get isEmpty => _queue.isEmpty; - - /// Returns true on overflow. - bool push(T val) { - bool overflow = false; - while (_queue.length >= _capacity) { - _queue.removeFirst(); - overflow = true; - } - _queue.addLast(val); - return overflow; - } - - /// Returns null when empty. - T pop() { - return _queue.isEmpty ? null : _queue.removeFirst(); - } - - /// Returns the number of discarded items resulting from resize. - int resize(int newSize) { - int result = 0; - - while (length > newSize) { - result += 1; - _queue.removeFirst(); - } - - _capacity = newSize; - - return result; - } -} - -/// Storage of channel messages until the channels are completely routed. +/// Remove and process all stored messages for a given channel. /// -/// Each channel has a finite buffer capacity and in a FIFO manner messages will -/// be deleted if the capacity is exceeded. The intention is that these buffers -/// will be drained once a callback is setup on the BinaryMessenger in the -/// Flutter framework. -class ChannelBuffers { - /// A somewhat arbitrary size that tries to balance handling typical - /// cases and not wasting memory. - static const int kDefaultBufferSize = 100; - - final Map> _messages = - >{}; - - /// Returns true on overflow. - bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { - _RingBuffer queue = _messages[channel]; - if (queue == null) { - queue = _RingBuffer(kDefaultBufferSize); - _messages[channel] = queue; - } - return queue.push(StoredMessage(data, callback)); - } - - /// Returns null on underflow. - StoredMessage pop(String channel) { - final _RingBuffer queue = _messages[channel]; - final StoredMessage result = queue?.pop(); - return result; - } - - bool isEmpty(String channel) { - final _RingBuffer queue = _messages[channel]; - return (queue == null) ? true : queue.isEmpty; - } - - void resize(String channel, int newSize) { - _RingBuffer queue = _messages[channel]; - if (queue == null) { - queue = _RingBuffer(newSize); - _messages[channel] = queue; - } else { - queue.resize(newSize); - } - } +/// This should be called once a channel is prepared to handle messages +/// (ie when a message handler is setup in the framework). +void drainChannelBuffer(String channel, DrainChannelCallback callback) async { + // noop, it may not be possible to store messages in the web_ui. } - -final ChannelBuffers channelBuffers = ChannelBuffers(); From afa7bb044fe48ac3853bf9d5abc641e7e4d44a16 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 11:32:47 -0700 Subject: [PATCH 24/46] Made ChannelBuffers private too. --- lib/ui/channel_buffers.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index da3daec8ce05c..54edc5ef8622b 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -73,7 +73,7 @@ class _RingBuffer { /// be deleted if the capacity is exceeded. The intention is that these buffers /// will be drained once a callback is setup on the BinaryMessenger in the /// Flutter framework. -class ChannelBuffers { +class _ChannelBuffers { /// A somewhat arbitrary size that tries to balance handling typical /// cases and not wasting memory. static const int kDefaultBufferSize = 100; @@ -138,4 +138,4 @@ void drainChannelBuffer(String channel, DrainChannelCallback callback) async { } } -final ChannelBuffers _channelBuffers = ChannelBuffers(); +final _ChannelBuffers _channelBuffers = _ChannelBuffers(); From 6b6dbb9c003e3a2d646a51aa7658e76a9170621d Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 15:49:19 -0700 Subject: [PATCH 25/46] Updated tests, made ChannelBuffers public and put the drain method on him. --- lib/ui/channel_buffers.dart | 30 ++++++++-------- lib/ui/hooks.dart | 2 +- lib/web_ui/lib/src/ui/channel_buffers.dart | 28 ++++++++++++--- testing/dart/channel_buffers_test.dart | 40 ++++++++++++++-------- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 54edc5ef8622b..d8386bf6db585 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -66,6 +66,8 @@ class _RingBuffer { } } +typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); + /// Storage of channel messages until the channels are completely routed /// (ie when a message handler is attached to the channel on the framework side). /// @@ -73,7 +75,7 @@ class _RingBuffer { /// be deleted if the capacity is exceeded. The intention is that these buffers /// will be drained once a callback is setup on the BinaryMessenger in the /// Flutter framework. -class _ChannelBuffers { +class ChannelBuffers { /// A somewhat arbitrary size that tries to balance handling typical /// cases and not wasting memory. static const int kDefaultBufferSize = 100; @@ -100,13 +102,13 @@ class _ChannelBuffers { } /// Returns null on underflow. - _StoredMessage pop(String channel) { + _StoredMessage _pop(String channel) { final _RingBuffer<_StoredMessage> queue = _messages[channel]; final _StoredMessage result = queue?.pop(); return result; } - bool isEmpty(String channel) { + bool _isEmpty(String channel) { final _RingBuffer<_StoredMessage> queue = _messages[channel]; return (queue == null) ? true : queue.isEmpty; } @@ -123,19 +125,17 @@ class _ChannelBuffers { } } } -} -typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); - -/// Remove and process all stored messages for a given channel. -/// -/// This should be called once a channel is prepared to handle messages -/// (ie when a message handler is setup in the framework). -void drainChannelBuffer(String channel, DrainChannelCallback callback) async { - while (!_channelBuffers.isEmpty(channel)) { - final _StoredMessage message = _channelBuffers.pop(channel); - await callback(message.data, message.callback); + /// Remove and process all stored messages for a given channel. + /// + /// This should be called once a channel is prepared to handle messages + /// (ie when a message handler is setup in the framework). + Future drain(String channel, DrainChannelCallback callback) async { + while (!_isEmpty(channel)) { + final _StoredMessage message = _pop(channel); + await callback(message.data, message.callback); + } } } -final _ChannelBuffers _channelBuffers = _ChannelBuffers(); +final ChannelBuffers channelBuffers = ChannelBuffers(); diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 8e45a22de6c5e..6fb6b3a1556e1 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -161,7 +161,7 @@ void _dispatchPlatformMessage(String name, ByteData data, int responseId) { }, ); } else { - _channelBuffers.push(name, data, (ByteData responseData) { + channelBuffers.push(name, data, (ByteData responseData) { window._respondToPlatformMessage(responseId, responseData); }); } diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index bcfb97276720e..2009c5e45a213 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -6,10 +6,28 @@ part of ui; typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); -/// Remove and process all stored messages for a given channel. +/// Storage of channel messages until the channels are completely routed +/// (ie when a message handler is attached to the channel on the framework side). /// -/// This should be called once a channel is prepared to handle messages -/// (ie when a message handler is setup in the framework). -void drainChannelBuffer(String channel, DrainChannelCallback callback) async { - // noop, it may not be possible to store messages in the web_ui. +/// Each channel has a finite buffer capacity and in a FIFO manner messages will +/// be deleted if the capacity is exceeded. The intention is that these buffers +/// will be drained once a callback is setup on the BinaryMessenger in the +/// Flutter framework. +class ChannelBuffers { + /// Returns true on overflow. + bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { + return true; + } + + void resize(String channel, int newSize) { + } + + /// Remove and process all stored messages for a given channel. + /// + /// This should be called once a channel is prepared to handle messages + /// (ie when a message handler is setup in the framework). + Future drain(String channel, DrainChannelCallback callback) async { + } } + +final ChannelBuffers channelBuffers = ChannelBuffers(); diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index 590e00f7cfb53..3cd998a6ba9d9 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -18,15 +18,16 @@ void main() { return utf8.decode(list); } - test('push pop', () async { + test('push drain', () async { String channel = "foo"; ByteData data = _makeByteData('bar'); ui.ChannelBuffers buffers = ui.ChannelBuffers(); ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; buffers.push(channel, data, callback); - ui.StoredMessage storedMessage = buffers.pop(channel); - expect(storedMessage.data, equals(data)); - expect(storedMessage.callback, equals(callback)); + await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + expect(drainedData, equals(data)); + expect(drainedCallback, equals(callback)); + }); }); test('empty', () async { @@ -34,14 +35,11 @@ void main() { ByteData data = _makeByteData('bar'); ui.ChannelBuffers buffers = ui.ChannelBuffers(); ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; - expect(buffers.isEmpty(channel), equals(true)); - buffers.push(channel, data, callback); - expect(buffers.isEmpty(channel), equals(false)); - }); - - test('pop', () async { - ui.ChannelBuffers buffers = ui.ChannelBuffers(); - expect(buffers.pop("channel"), equals(null)); + bool didCall = false; + await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + didCall = true; + }); + expect(didCall, equals(false)); }); test('overflow', () async { @@ -57,7 +55,14 @@ void main() { expect(buffers.push(channel, two, callback), equals(false)); expect(buffers.push(channel, three, callback), equals(false)); expect(buffers.push(channel, four, callback), equals(true)); - expect(_getString(buffers.pop(channel).data), equals('two')); + int counter = 0; + await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + if (counter++ == 0) { + expect(drainedData, equals(two)); + expect(drainedCallback, equals(callback)); + } + }); + expect(counter, equals(3)); }); test('resize drop', () async { @@ -69,6 +74,13 @@ void main() { expect(buffers.push(channel, one, callback), equals(false)); expect(buffers.push(channel, two, callback), equals(false)); buffers.resize(channel, 1); - expect(_getString(buffers.pop(channel).data), equals('two')); + int counter = 0; + await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + if (counter++ == 0) { + expect(drainedData, equals(two)); + expect(drainedCallback, equals(callback)); + } + }); + expect(counter, equals(1)); }); } From bcc5ea7ab1244cb0ca6907eaba783bbde8060bea Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 16:08:53 -0700 Subject: [PATCH 26/46] Made it such that the callback for messages gets called even if they get dropped. This matches the current semantics. --- lib/ui/channel_buffers.dart | 29 +++++++++++++++++++---- testing/dart/channel_buffers_test.dart | 32 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index d8386bf6db585..48e744588784e 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -25,6 +25,7 @@ class _StoredMessage { class _RingBuffer { final collection.ListQueue _queue; int _capacity; + Function(T) _dropItemCallback; _RingBuffer(this._capacity) : _queue = collection.ListQueue(_capacity); @@ -35,11 +36,18 @@ class _RingBuffer { bool get isEmpty => _queue.isEmpty; + bool set dropItemCallback(Function(T) callback) { + _dropItemCallback = callback; + } + /// Returns true on overflow. bool push(T val) { bool overflow = false; while (_queue.length >= _capacity) { - _queue.removeFirst(); + T item = _queue.removeFirst(); + if (_dropItemCallback != null) { + _dropItemCallback(item); + } overflow = true; } _queue.addLast(val); @@ -57,7 +65,10 @@ class _RingBuffer { while (length > newSize) { result += 1; - _queue.removeFirst(); + T item = _queue.removeFirst(); + if (_dropItemCallback != null) { + _dropItemCallback(item); + } } _capacity = newSize; @@ -83,11 +94,21 @@ class ChannelBuffers { final Map> _messages = >{}; + _RingBuffer<_StoredMessage> _makeRingBuffer(int size) { + var result = _RingBuffer<_StoredMessage>(size); + result.dropItemCallback = _onDropItem; + return result; + } + + void _onDropItem(_StoredMessage message) { + message.callback(null); + } + /// Returns true on overflow. bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { _RingBuffer<_StoredMessage> queue = _messages[channel]; if (queue == null) { - queue = _RingBuffer<_StoredMessage>(kDefaultBufferSize); + queue = _makeRingBuffer(kDefaultBufferSize); _messages[channel] = queue; } final bool result = queue.push(_StoredMessage(data, callback)); @@ -116,7 +137,7 @@ class ChannelBuffers { void resize(String channel, int newSize) { _RingBuffer<_StoredMessage> queue = _messages[channel]; if (queue == null) { - queue = _RingBuffer<_StoredMessage>(newSize); + queue = _makeRingBuffer(newSize); _messages[channel] = queue; } else { final int numberOfDroppedMessages = queue.resize(newSize); diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index 3cd998a6ba9d9..fe47e899c6279 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -83,4 +83,36 @@ void main() { }); expect(counter, equals(1)); }); + + test('resize dropping calls callback', () async { + String channel = "foo"; + ByteData one = _makeByteData('one'); + ByteData two = _makeByteData('two'); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool didCallCallback = false; + ui.PlatformMessageResponseCallback oneCallback = (ByteData responseData) { + didCallCallback = true; + }; + ui.PlatformMessageResponseCallback twoCallback = (ByteData responseData) {}; + expect(buffers.push(channel, one, oneCallback), equals(false)); + expect(buffers.push(channel, two, twoCallback), equals(false)); + buffers.resize(channel, 1); + expect(didCallCallback, equals(true)); + }); + + test('overflow calls callback', () async { + String channel = "foo"; + ByteData one = _makeByteData('one'); + ByteData two = _makeByteData('two'); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + bool didCallCallback = false; + ui.PlatformMessageResponseCallback oneCallback = (ByteData responseData) { + didCallCallback = true; + }; + ui.PlatformMessageResponseCallback twoCallback = (ByteData responseData) {}; + buffers.resize(channel, 1); + expect(buffers.push(channel, one, oneCallback), equals(false)); + expect(buffers.push(channel, two, twoCallback), equals(true)); + expect(didCallCallback, equals(true)); + }); } From 3ff518c7036e26af863ff093b988a103e5196038 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 16:14:04 -0700 Subject: [PATCH 27/46] Moved the default queue size to 1. --- lib/ui/channel_buffers.dart | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 48e744588784e..f86391feb443d 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -87,9 +87,18 @@ typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageRe /// will be drained once a callback is setup on the BinaryMessenger in the /// Flutter framework. class ChannelBuffers { - /// A somewhat arbitrary size that tries to balance handling typical - /// cases and not wasting memory. - static const int kDefaultBufferSize = 100; + /// By default we store one message per channel. There are tradeoffs associated + /// with any size. The correct size should be chosen for the semantics of your + /// channel. + /// + /// Size 0 implies you want to ignore any message that gets sent before the engine + /// is ready (keeping in mind there is no way to know when the engine is ready). + /// + /// Size 1 implies that you only care about the most recent value. + /// + /// Size >1 means you want to process every single message and want to chose a + /// buffer size that will avoid any overflows. + static const int kDefaultBufferSize = 1; final Map> _messages = >{}; From 8d4a1e40a6998bf30be0c61d256c79a5b4e4640c Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 16:16:16 -0700 Subject: [PATCH 28/46] fixed formatting in docstring --- lib/ui/channel_buffers.dart | 4 ++-- lib/web_ui/lib/src/ui/channel_buffers.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index f86391feb443d..0b6ba9391d42a 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -79,8 +79,8 @@ class _RingBuffer { typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); -/// Storage of channel messages until the channels are completely routed -/// (ie when a message handler is attached to the channel on the framework side). +/// Storage of channel messages until the channels are completely routed, +/// i.e. when a message handler is attached to the channel on the framework side. /// /// Each channel has a finite buffer capacity and in a FIFO manner messages will /// be deleted if the capacity is exceeded. The intention is that these buffers diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index 2009c5e45a213..a42cf34ba95a4 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -6,8 +6,8 @@ part of ui; typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); -/// Storage of channel messages until the channels are completely routed -/// (ie when a message handler is attached to the channel on the framework side). +/// Storage of channel messages until the channels are completely routed, +/// i.e. when a message handler is attached to the channel on the framework side. /// /// Each channel has a finite buffer capacity and in a FIFO manner messages will /// be deleted if the capacity is exceeded. The intention is that these buffers From 8e6ee3b6d40b7648c04a90a96887af775b5f54d4 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 17:36:08 -0700 Subject: [PATCH 29/46] addressed linter errors --- lib/ui/channel_buffers.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 0b6ba9391d42a..5f1871f65bcea 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -36,7 +36,7 @@ class _RingBuffer { bool get isEmpty => _queue.isEmpty; - bool set dropItemCallback(Function(T) callback) { + void set dropItemCallback(Function(T) callback) { _dropItemCallback = callback; } @@ -44,7 +44,7 @@ class _RingBuffer { bool push(T val) { bool overflow = false; while (_queue.length >= _capacity) { - T item = _queue.removeFirst(); + final T item = _queue.removeFirst(); if (_dropItemCallback != null) { _dropItemCallback(item); } @@ -65,7 +65,7 @@ class _RingBuffer { while (length > newSize) { result += 1; - T item = _queue.removeFirst(); + final T item = _queue.removeFirst(); if (_dropItemCallback != null) { _dropItemCallback(item); } @@ -104,7 +104,7 @@ class ChannelBuffers { >{}; _RingBuffer<_StoredMessage> _makeRingBuffer(int size) { - var result = _RingBuffer<_StoredMessage>(size); + final _RingBuffer<_StoredMessage> result = _RingBuffer<_StoredMessage>(size); result.dropItemCallback = _onDropItem; return result; } From 3e3c38de5d3a22d215e793b938252ce37f3cddf3 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 12 Sep 2019 18:04:07 -0700 Subject: [PATCH 30/46] removed return type from setter --- lib/ui/channel_buffers.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 5f1871f65bcea..a86868f16ce73 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -36,7 +36,7 @@ class _RingBuffer { bool get isEmpty => _queue.isEmpty; - void set dropItemCallback(Function(T) callback) { + set dropItemCallback(Function(T) callback) { _dropItemCallback = callback; } From a4204c92cd1c09c35c50695f9821b30d4bba264d Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 13 Sep 2019 08:35:30 -0700 Subject: [PATCH 31/46] Fixed tests that started failing after the buffer size was set to 1. --- testing/dart/channel_buffers_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index fe47e899c6279..57afdc973b836 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -70,6 +70,7 @@ void main() { ByteData one = _makeByteData('one'); ByteData two = _makeByteData('two'); ui.ChannelBuffers buffers = ui.ChannelBuffers(); + buffers.resize(channel, 100); ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; expect(buffers.push(channel, one, callback), equals(false)); expect(buffers.push(channel, two, callback), equals(false)); @@ -94,6 +95,7 @@ void main() { didCallCallback = true; }; ui.PlatformMessageResponseCallback twoCallback = (ByteData responseData) {}; + buffers.resize(channel, 100); expect(buffers.push(channel, one, oneCallback), equals(false)); expect(buffers.push(channel, two, twoCallback), equals(false)); buffers.resize(channel, 1); From 1527c3bd628ea0d9c07f67357625329d4bd81c18 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 13 Sep 2019 08:39:10 -0700 Subject: [PATCH 32/46] Added todo --- lib/ui/channel_buffers.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a86868f16ce73..1b4b980db1a15 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -122,6 +122,8 @@ class ChannelBuffers { } final bool result = queue.push(_StoredMessage(data, callback)); if (result) { + // TODO(aaclarke): Update this message to include instructions on how to resize + // the buffer once that is available to users. _Logger._printString('Overflow on channel: $channel. ' 'Messages on this channel are being sent faster ' 'than they are being processed which is resulting ' From 837ed7e9b94c05040c0a1976e51414d213687063 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 13 Sep 2019 08:43:17 -0700 Subject: [PATCH 33/46] Made the web_ui call the callback (behave like size zero for everything). --- lib/web_ui/lib/src/ui/channel_buffers.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index a42cf34ba95a4..c18cd7f367338 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -16,6 +16,7 @@ typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageRe class ChannelBuffers { /// Returns true on overflow. bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { + callback(null); return true; } From 23a9ce9bc9da692dce3c05a9c69294428ea76a0a Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 13 Sep 2019 12:39:17 -0700 Subject: [PATCH 34/46] removed newline --- lib/web_ui/lib/src/ui/channel_buffers.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index c18cd7f367338..f1e6da7f230ca 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -20,8 +20,7 @@ class ChannelBuffers { return true; } - void resize(String channel, int newSize) { - } + void resize(String channel, int newSize) {} /// Remove and process all stored messages for a given channel. /// From 741981b5efe0c4a91ecaa679486a1a7bbcb6cd97 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 17:25:09 -0700 Subject: [PATCH 35/46] added some docstrings, removed duplication --- lib/ui/channel_buffers.dart | 38 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 1b4b980db1a15..63ab255915164 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -25,33 +25,36 @@ class _StoredMessage { class _RingBuffer { final collection.ListQueue _queue; int _capacity; + /// A callback that get's called when items are ejected from the [_RingBuffer] + /// by way of an overflow or a resizing. Function(T) _dropItemCallback; _RingBuffer(this._capacity) : _queue = collection.ListQueue(_capacity); + /// Returns the number of items in the [_RingBuffer]. int get length => _queue.length; + /// Returns the man number of items that can be stored in the [_RingBuffer]. int get capacity => _capacity; + /// Returns true if there are no items in the [_RingBuffer]. bool get isEmpty => _queue.isEmpty; + /// Setter for the _dropItemCallback field. set dropItemCallback(Function(T) callback) { _dropItemCallback = callback; } /// Returns true on overflow. bool push(T val) { - bool overflow = false; - while (_queue.length >= _capacity) { - final T item = _queue.removeFirst(); - if (_dropItemCallback != null) { - _dropItemCallback(item); - } - overflow = true; + if (_capacity <= 0) { + return true; + } else { + final int overflowCount = _dropOverflowItems(_capacity - 1); + _queue.addLast(val); + return overflowCount > 0; } - _queue.addLast(val); - return overflow; } /// Returns null when empty. @@ -59,21 +62,24 @@ class _RingBuffer { return _queue.isEmpty ? null : _queue.removeFirst(); } - /// Returns the number of discarded items resulting from resize. - int resize(int newSize) { + /// Removes items until then length reaches [lengthLimit] and returns + /// the number of items removed. + int _dropOverflowItems(int lengthLimit) { int result = 0; - - while (length > newSize) { - result += 1; + while (_queue.length > lengthLimit) { final T item = _queue.removeFirst(); if (_dropItemCallback != null) { _dropItemCallback(item); } + result += 1; } + return result; + } + /// Returns the number of discarded items resulting from resize. + int resize(int newSize) { _capacity = newSize; - - return result; + return _dropOverflowItems(newSize); } } From 571594ec0f9e8f154acca5c8c2f2fa82da7375c4 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 17:30:52 -0700 Subject: [PATCH 36/46] added test for zero size --- testing/dart/channel_buffers_test.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index 57afdc973b836..ee5312484ee12 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -30,6 +30,20 @@ void main() { }); }); + test('push drain zero', () async { + String channel = "foo"; + ByteData data = _makeByteData('bar'); + ui.ChannelBuffers buffers = ui.ChannelBuffers(); + ui.PlatformMessageResponseCallback callback = (ByteData responseData) {}; + buffers.resize(channel, 0); + buffers.push(channel, data, callback); + bool didCall = false; + await buffers.drain(channel, (ByteData drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + didCall = true; + }); + expect(didCall, equals(false)); + }); + test('empty', () async { String channel = "foo"; ByteData data = _makeByteData('bar'); From 478520fbe63530cb040977cac5b9a148809a45f1 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 17:32:22 -0700 Subject: [PATCH 37/46] added docstring --- lib/ui/channel_buffers.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 63ab255915164..83e9482006a0b 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -83,6 +83,7 @@ class _RingBuffer { } } +/// A callback for [ChannelBuffers.drain]. typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); /// Storage of channel messages until the channels are completely routed, From 8569f21a0769531868926ee9f2a711e349c1ffc3 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 17:54:05 -0700 Subject: [PATCH 38/46] update docstring and error message --- lib/ui/channel_buffers.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 83e9482006a0b..e45654c008326 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -93,6 +93,10 @@ typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageRe /// be deleted if the capacity is exceeded. The intention is that these buffers /// will be drained once a callback is setup on the BinaryMessenger in the /// Flutter framework. +/// +/// Clients of Flutter shouldn't need to allocate their own ChannelBuffers +/// and should only access this package's [channelBuffers] if they are writing +/// their own custom [BinaryMessenger]. class ChannelBuffers { /// By default we store one message per channel. There are tradeoffs associated /// with any size. The correct size should be chosen for the semantics of your @@ -132,10 +136,9 @@ class ChannelBuffers { // TODO(aaclarke): Update this message to include instructions on how to resize // the buffer once that is available to users. _Logger._printString('Overflow on channel: $channel. ' - 'Messages on this channel are being sent faster ' - 'than they are being processed which is resulting ' - 'in the dropping of messages. The engine may not be ' - 'running or you need to adjust the buffer size.'); + 'Messages on this channel are being discarded. ' + 'The engine may not be running or you need to adjust ' + 'the buffer size if of the channel.'); } return result; } From 96761f804cad107259c0eed172c1751a2d0191cc Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 18:10:01 -0700 Subject: [PATCH 39/46] added docstringg --- lib/ui/channel_buffers.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index e45654c008326..a6ccee2c0f380 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -155,6 +155,10 @@ class ChannelBuffers { return (queue == null) ? true : queue.isEmpty; } + /// Changes the capacity of the queue associated with the given channel. + /// + /// This could result in the dropping of messages if the newSize is less + /// than the current length of the queue. void resize(String channel, int newSize) { _RingBuffer<_StoredMessage> queue = _messages[channel]; if (queue == null) { From adcd6417570f1dd6454c702866863bd222d9089f Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 16 Sep 2019 18:17:15 -0700 Subject: [PATCH 40/46] Updated web_ui documentation --- lib/ui/channel_buffers.dart | 2 +- lib/web_ui/lib/src/ui/channel_buffers.dart | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a6ccee2c0f380..a548b1bd0f7be 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -83,7 +83,7 @@ class _RingBuffer { } } -/// A callback for [ChannelBuffers.drain]. +/// A callback for [ChannelBuffers.drain], called as it pops stored messages. typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); /// Storage of channel messages until the channels are completely routed, diff --git a/lib/web_ui/lib/src/ui/channel_buffers.dart b/lib/web_ui/lib/src/ui/channel_buffers.dart index f1e6da7f230ca..e872d1b9d2691 100644 --- a/lib/web_ui/lib/src/ui/channel_buffers.dart +++ b/lib/web_ui/lib/src/ui/channel_buffers.dart @@ -4,28 +4,24 @@ part of ui; +/// A callback for [ChannelBuffers.drain], called as it pops stored messages. typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); -/// Storage of channel messages until the channels are completely routed, -/// i.e. when a message handler is attached to the channel on the framework side. -/// -/// Each channel has a finite buffer capacity and in a FIFO manner messages will -/// be deleted if the capacity is exceeded. The intention is that these buffers -/// will be drained once a callback is setup on the BinaryMessenger in the -/// Flutter framework. +/// Web implementation of [ChannelBuffers]. Currently it just drops all messages +/// to match legacy behavior and acts as if all caches are size zero. class ChannelBuffers { - /// Returns true on overflow. + /// Always returns true to denote an overflow. bool push(String channel, ByteData data, PlatformMessageResponseCallback callback) { callback(null); return true; } + /// Noop in web_ui, caches are always size zero. void resize(String channel, int newSize) {} /// Remove and process all stored messages for a given channel. /// - /// This should be called once a channel is prepared to handle messages - /// (ie when a message handler is setup in the framework). + /// A noop in web_ui since all caches are size zero. Future drain(String channel, DrainChannelCallback callback) async { } } From 24cf9277d793401740e5ba6af48f5b967425a6b8 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 10:20:40 -0700 Subject: [PATCH 41/46] shuffled around docstrings to collapse the setter and the ivar. --- lib/ui/channel_buffers.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index a548b1bd0f7be..45df87747242e 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -24,10 +24,6 @@ class _StoredMessage { /// A fixed-size circular queue. class _RingBuffer { final collection.ListQueue _queue; - int _capacity; - /// A callback that get's called when items are ejected from the [_RingBuffer] - /// by way of an overflow or a resizing. - Function(T) _dropItemCallback; _RingBuffer(this._capacity) : _queue = collection.ListQueue(_capacity); @@ -35,13 +31,16 @@ class _RingBuffer { /// Returns the number of items in the [_RingBuffer]. int get length => _queue.length; - /// Returns the man number of items that can be stored in the [_RingBuffer]. + /// The number of items that can be stored in the [_RingBuffer]. + int _capacity; int get capacity => _capacity; /// Returns true if there are no items in the [_RingBuffer]. bool get isEmpty => _queue.isEmpty; - /// Setter for the _dropItemCallback field. + /// A callback that get's called when items are ejected from the [_RingBuffer] + /// by way of an overflow or a resizing. + Function(T) _dropItemCallback; set dropItemCallback(Function(T) callback) { _dropItemCallback = callback; } From 67aecdb167f6062b77f6297c23f61823288151d5 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 10:29:43 -0700 Subject: [PATCH 42/46] added more documentation --- lib/ui/channel_buffers.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 45df87747242e..434b122a05668 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -12,17 +12,19 @@ class _StoredMessage { /// is handled. _StoredMessage(this._data, this._callback); + /// Representation of the message's payload. final ByteData _data; - final PlatformMessageResponseCallback _callback; - - /// Getter for _data field, represents the message's payload. ByteData get data => _data; - /// Getter for the _callback field, to be called when the message is received. + + /// Callback to be called when the message is received. + final PlatformMessageResponseCallback _callback; PlatformMessageResponseCallback get callback => _callback; } /// A fixed-size circular queue. class _RingBuffer { + /// The underlying data for the RingBuffer. ListQueue's dynamically resize, + /// [_RingBuffer]s do not. final collection.ListQueue _queue; _RingBuffer(this._capacity) @@ -110,6 +112,7 @@ class ChannelBuffers { /// buffer size that will avoid any overflows. static const int kDefaultBufferSize = 1; + /// A mapping between a channel name and its associated [_RingBuffer]. final Map> _messages = >{}; @@ -183,4 +186,10 @@ class ChannelBuffers { } } +/// [ChannelBuffer]s that allow the storage of messages between the +/// Engine and the Framework. Typically messages that can't be delivered +/// are stored here until the Framework is able to process them. +/// +/// See also: +/// * [BinaryMessenger] - The place where ChannelBuffers are typically read. final ChannelBuffers channelBuffers = ChannelBuffers(); From b13a3dc72bd63fdb7778ef6560c6b9eea0888b8d Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 10:33:06 -0700 Subject: [PATCH 43/46] updated variable name, tweaked log message --- lib/ui/channel_buffers.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 434b122a05668..204ab613ce739 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -133,16 +133,16 @@ class ChannelBuffers { queue = _makeRingBuffer(kDefaultBufferSize); _messages[channel] = queue; } - final bool result = queue.push(_StoredMessage(data, callback)); - if (result) { + final bool didOverflow = queue.push(_StoredMessage(data, callback)); + if (didOverflow) { // TODO(aaclarke): Update this message to include instructions on how to resize // the buffer once that is available to users. _Logger._printString('Overflow on channel: $channel. ' - 'Messages on this channel are being discarded. ' + 'Messages on this channel are being discarded in FIFO fashion. ' 'The engine may not be running or you need to adjust ' 'the buffer size if of the channel.'); } - return result; + return didOverflow; } /// Returns null on underflow. From 282bff0908e1977b16ab542795876d8c00d45eaa Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 10:47:10 -0700 Subject: [PATCH 44/46] updated docstrings --- lib/ui/channel_buffers.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 204ab613ce739..854a9c442832b 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -84,7 +84,7 @@ class _RingBuffer { } } -/// A callback for [ChannelBuffers.drain], called as it pops stored messages. +/// Signature for [ChannelBuffers.drain]. typedef DrainChannelCallback = Future Function(ByteData, PlatformMessageResponseCallback); /// Storage of channel messages until the channels are completely routed, @@ -159,7 +159,7 @@ class ChannelBuffers { /// Changes the capacity of the queue associated with the given channel. /// - /// This could result in the dropping of messages if the newSize is less + /// This could result in the dropping of messages if newSize is less /// than the current length of the queue. void resize(String channel, int newSize) { _RingBuffer<_StoredMessage> queue = _messages[channel]; @@ -177,7 +177,7 @@ class ChannelBuffers { /// Remove and process all stored messages for a given channel. /// /// This should be called once a channel is prepared to handle messages - /// (ie when a message handler is setup in the framework). + /// (i.e. when a message handler is setup in the framework). Future drain(String channel, DrainChannelCallback callback) async { while (!_isEmpty(channel)) { final _StoredMessage message = _pop(channel); From a0a4c0e253688b8e07ae474bcef2b91a1ba24526 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 11:02:58 -0700 Subject: [PATCH 45/46] Made it so that channel buffer's error messages are only printed in debug builds. --- lib/ui/channel_buffers.dart | 11 ++++++----- lib/ui/dart_runtime_hooks.cc | 7 +++++++ lib/ui/natives.dart | 5 +++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 854a9c442832b..26ba96d1ebb6d 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -136,11 +136,12 @@ class ChannelBuffers { final bool didOverflow = queue.push(_StoredMessage(data, callback)); if (didOverflow) { // TODO(aaclarke): Update this message to include instructions on how to resize - // the buffer once that is available to users. - _Logger._printString('Overflow on channel: $channel. ' - 'Messages on this channel are being discarded in FIFO fashion. ' - 'The engine may not be running or you need to adjust ' - 'the buffer size if of the channel.'); + // the buffer once that is available to users and print in all engine builds + // after we verify that dropping messages isn't part of normal execution. + _printDebug('Overflow on channel: $channel. ' + 'Messages on this channel are being discarded in FIFO fashion. ' + 'The engine may not be running or you need to adjust ' + 'the buffer size if of the channel.'); } return didOverflow; } diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index 1b08181313543..6f3d8cd7a6851 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -49,6 +49,7 @@ namespace flutter { #define BUILTIN_NATIVE_LIST(V) \ V(Logger_PrintString, 1) \ + V(Logger_PrintDebugString, 1) \ V(SaveCompilationTrace, 0) \ V(ScheduleMicrotask, 1) \ V(GetCallbackHandle, 1) \ @@ -152,6 +153,12 @@ void DartRuntimeHooks::Install(bool is_ui_isolate, InitDartIO(builtin, script_uri); } +void Logger_PrintDebugString(Dart_NativeArguments args) { +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + Logger_PrintString(args); +#endif +} + // Implementation of native functions which are used for some // test/debug functionality in standalone dart mode. void Logger_PrintString(Dart_NativeArguments args) { diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index b48d8d440541a..841c925993d62 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -11,8 +11,13 @@ void _print(dynamic arg) { _Logger._printString(arg.toString()); } +void _printDebug(dynamic arg) { + _Logger._printDebugString(arg.toString()); +} + class _Logger { static void _printString(String s) native 'Logger_PrintString'; + static void _printDebugString(String s) native 'Logger_PrintDebugString'; } // If we actually run on big endian machines, we'll need to do something smarter From 26d9deb9943895f1b6ff69e15eb62d6b1598543f Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 17 Sep 2019 11:08:11 -0700 Subject: [PATCH 46/46] fixed formatting of c++ code --- lib/ui/dart_runtime_hooks.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index 6f3d8cd7a6851..000c4c43726a1 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -47,12 +47,12 @@ namespace flutter { #define DECLARE_FUNCTION(name, count) \ extern void name(Dart_NativeArguments args); -#define BUILTIN_NATIVE_LIST(V) \ - V(Logger_PrintString, 1) \ - V(Logger_PrintDebugString, 1) \ - V(SaveCompilationTrace, 0) \ - V(ScheduleMicrotask, 1) \ - V(GetCallbackHandle, 1) \ +#define BUILTIN_NATIVE_LIST(V) \ + V(Logger_PrintString, 1) \ + V(Logger_PrintDebugString, 1) \ + V(SaveCompilationTrace, 0) \ + V(ScheduleMicrotask, 1) \ + V(GetCallbackHandle, 1) \ V(GetCallbackFromHandle, 1) BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);