Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(190)

Side by Side Diff: pkg/analysis_server/lib/src/channel/byte_stream_channel.dart

Issue 758833004: Coalesce analysis server output messages into a single write call. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library channel.byte_stream; 5 library channel.byte_stream;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:convert'; 8 import 'dart:convert';
9 import 'dart:io'; 9 import 'dart:io';
10 10
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 return responseStream.firstWhere((Response response) => response.id == id); 53 return responseStream.firstWhere((Response response) => response.id == id);
54 } 54 }
55 } 55 }
56 56
57 /** 57 /**
58 * Instances of the class [ByteStreamServerChannel] implement a 58 * Instances of the class [ByteStreamServerChannel] implement a
59 * [ServerCommunicationChannel] that uses a stream and a sink (typically, 59 * [ServerCommunicationChannel] that uses a stream and a sink (typically,
60 * standard input and standard output) to communicate with clients. 60 * standard input and standard output) to communicate with clients.
61 */ 61 */
62 class ByteStreamServerChannel implements ServerCommunicationChannel { 62 class ByteStreamServerChannel implements ServerCommunicationChannel {
63 /**
64 * Value of [_outputState] indicating that there is no outstanding data in
65 * [_pendingOutput], and that the most recent flush of [_output] has
66 * completed.
67 */
68 static const int _STATE_IDLE = 0;
69
70 /**
71 * Value of [_outputState] indicating that there is outstanding data in
72 * [_pendingOutput], and that the most recent flush of [_output] has
73 * completed; therefore a microtask has been scheduled to send the data.
74 */
75 static const int _STATE_MICROTASK_PENDING = 1;
76
77 /**
78 * Value of [_outputState] indicating that data has been sent to the
79 * [_output] stream and flushed, but the flush has not completed, so we must
80 * wait for it to complete before sending more data. There may or may not be
81 * outstanding data in [_pendingOutput].
82 */
83 static const int _STATE_FLUSH_PENDING = 2;
84
63 final Stream input; 85 final Stream input;
64 final IOSink output; 86
87 final IOSink _output;
65 88
66 /** 89 /**
67 * Completer that will be signalled when the input stream is closed. 90 * Completer that will be signalled when the input stream is closed.
68 */ 91 */
69 final Completer _closed = new Completer(); 92 final Completer _closed = new Completer();
70 93
71 ByteStreamServerChannel(this.input, this.output); 94 /**
95 * State of the output stream (see constants above).
96 */
97 int _outputState = _STATE_IDLE;
98
99 /**
100 * List of strings that need to be sent to [_output] at the next available
101 * opportunity.
102 */
103 List<String> _pendingOutput = <String>[];
104
105 /**
106 * True if [close] has been called.
107 */
108 bool _closeRequested = false;
109
110 ByteStreamServerChannel(this.input, this._output);
72 111
73 /** 112 /**
74 * Future that will be completed when the input stream is closed. 113 * Future that will be completed when the input stream is closed.
75 */ 114 */
76 Future get closed { 115 Future get closed {
77 return _closed.future; 116 return _closed.future;
78 } 117 }
79 118
80 @override 119 @override
81 void close() { 120 void close() {
82 output.flush().then((_) { 121 if (!_closeRequested) {
83 if (!_closed.isCompleted) { 122 _closeRequested = true;
123 if (_outputState == _STATE_IDLE) {
124 assert(!_closed.isCompleted);
84 _closed.complete(); 125 _closed.complete();
126 } else {
127 // Nothing to do. [_flushCompleted] will call _closed.complete() after
128 // the flush completes.
85 } 129 }
86 }); 130 }
87 } 131 }
88 132
89 @override 133 @override
90 void listen(void onRequest(Request request), {Function onError, void 134 void listen(void onRequest(Request request), {Function onError, void
91 onDone()}) { 135 onDone()}) {
92 input.transform( 136 input.transform(
93 (new Utf8Codec()).decoder).transform( 137 (new Utf8Codec()).decoder).transform(
94 new LineSplitter()).listen( 138 new LineSplitter()).listen(
95 (String data) => _readRequest(data, onRequest), 139 (String data) => _readRequest(data, onRequest),
96 onError: onError, 140 onError: onError,
97 onDone: () { 141 onDone: () {
98 close(); 142 close();
99 onDone(); 143 onDone();
100 }); 144 });
101 } 145 }
102 146
103 @override 147 @override
104 void sendNotification(Notification notification) { 148 void sendNotification(Notification notification) {
105 // Don't send any further notifications after the communication channel is 149 // Don't send any further notifications after the communication channel is
106 // closed. 150 // closed.
107 if (_closed.isCompleted) { 151 if (_closeRequested) {
108 return; 152 return;
109 } 153 }
110 ServerCommunicationChannel.ToJson.start(); 154 ServerCommunicationChannel.ToJson.start();
111 String jsonEncoding = JSON.encode(notification.toJson()); 155 String jsonEncoding = JSON.encode(notification.toJson());
112 ServerCommunicationChannel.ToJson.stop(); 156 ServerCommunicationChannel.ToJson.stop();
113 output.write(jsonEncoding + '\n'); 157 _outputLine(jsonEncoding);
114 } 158 }
115 159
116 @override 160 @override
117 void sendResponse(Response response) { 161 void sendResponse(Response response) {
118 // Don't send any further responses after the communication channel is 162 // Don't send any further responses after the communication channel is
119 // closed. 163 // closed.
120 if (_closed.isCompleted) { 164 if (_closeRequested) {
121 return; 165 return;
122 } 166 }
123 ServerCommunicationChannel.ToJson.start(); 167 ServerCommunicationChannel.ToJson.start();
124 String jsonEncoding = JSON.encode(response.toJson()); 168 String jsonEncoding = JSON.encode(response.toJson());
125 ServerCommunicationChannel.ToJson.stop(); 169 ServerCommunicationChannel.ToJson.stop();
126 output.write(jsonEncoding + '\n'); 170 _outputLine(jsonEncoding);
127 } 171 }
128 172
129 /** 173 /**
174 * Callback invoked after a flush of [_output] completes. Closes the stream
175 * if necessary. Otherwise schedules additional pending output.
176 */
177 void _flushCompleted(_) {
178 assert(_outputState == _STATE_FLUSH_PENDING);
179 if (_pendingOutput.isNotEmpty) {
180 _output.write(_pendingOutput.join());
181 _output.flush().then(_flushCompleted);
182 _pendingOutput.clear();
183 // Since we've done another flush, stay in _STATE_FLUSH_PENDING.
184 } else {
185 _outputState = _STATE_IDLE;
186 if (_closeRequested) {
187 assert(!_closed.isCompleted);
188 _closed.complete();
189 }
190 }
191 }
192
193 /**
194 * Microtask that writes pending output to the output stream and flushes it.
195 */
196 void _microtask() {
197 assert(_outputState == _STATE_MICROTASK_PENDING);
198 _output.write(_pendingOutput.join());
199 _output.flush().then(_flushCompleted);
200 _pendingOutput.clear();
201 _outputState = _STATE_FLUSH_PENDING;
202 }
203
204 /**
205 * Send the string [s] to [_output] followed by a newline.
206 */
207 void _outputLine(String s) {
208 _pendingOutput.add(s);
209 _pendingOutput.add('\n');
210 if (_outputState == _STATE_IDLE) {
211 // Don't send the output just yet; schedule a microtask to do it, so that
212 // if caller decides to output additional lines, they will get sent in
213 // the same call to _output.write().
214 new Future.microtask(_microtask);
215 _outputState = _STATE_MICROTASK_PENDING;
216 }
217 }
218
219 /**
130 * Read a request from the given [data] and use the given function to handle 220 * Read a request from the given [data] and use the given function to handle
131 * the request. 221 * the request.
132 */ 222 */
133 void _readRequest(Object data, void onRequest(Request request)) { 223 void _readRequest(Object data, void onRequest(Request request)) {
134 // Ignore any further requests after the communication channel is closed. 224 // Ignore any further requests after the communication channel is closed.
135 if (_closed.isCompleted) { 225 if (_closed.isCompleted) {
136 return; 226 return;
137 } 227 }
138 // Parse the string as a JSON descriptor and process the resulting 228 // Parse the string as a JSON descriptor and process the resulting
139 // structure as a request. 229 // structure as a request.
140 ServerCommunicationChannel.FromJson.start(); 230 ServerCommunicationChannel.FromJson.start();
141 Request request = new Request.fromString(data); 231 Request request = new Request.fromString(data);
142 ServerCommunicationChannel.FromJson.stop(); 232 ServerCommunicationChannel.FromJson.stop();
143 if (request == null) { 233 if (request == null) {
144 sendResponse(new Response.invalidRequestFormat()); 234 sendResponse(new Response.invalidRequestFormat());
145 return; 235 return;
146 } 236 }
147 onRequest(request); 237 onRequest(request);
148 } 238 }
149 } 239 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698