OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 part of core; | 5 part of core; |
6 | 6 |
7 class _MojoHandleNatives { | 7 class _MojoHandleNatives { |
8 static int register(MojoHandle handle) native "MojoHandle_Register"; | 8 static int register(MojoEventStream eventStream) native "MojoHandle_Register"; |
9 static int close(int handle) native "MojoHandle_Close"; | 9 static int close(int handle) native "MojoHandle_Close"; |
10 static List wait(int handle, int signals, int deadline) | 10 static List wait(int handle, int signals, int deadline) |
11 native "MojoHandle_Wait"; | 11 native "MojoHandle_Wait"; |
12 static List waitMany( | 12 static List waitMany( |
13 List<int> handles, List<int> signals, int deadline) | 13 List<int> handles, List<int> signals, int deadline) |
14 native "MojoHandle_WaitMany"; | 14 native "MojoHandle_WaitMany"; |
15 } | 15 } |
16 | 16 |
17 | 17 |
18 class RawMojoHandle { | 18 class MojoHandle { |
19 static const int INVALID = 0; | 19 static const int INVALID = 0; |
20 static const int DEADLINE_INDEFINITE = -1; | 20 static const int DEADLINE_INDEFINITE = -1; |
21 | 21 |
22 int h; | 22 int h; |
23 | 23 |
24 RawMojoHandle(this.h); | 24 MojoHandle(this.h); |
25 | 25 |
26 MojoResult close() { | 26 MojoResult close() { |
27 int result = _MojoHandleNatives.close(h); | 27 int result = _MojoHandleNatives.close(h); |
28 h = INVALID; | 28 h = INVALID; |
29 return new MojoResult(result); | 29 return new MojoResult(result); |
30 } | 30 } |
31 | 31 |
32 MojoWaitResult wait(int signals, int deadline) { | 32 MojoWaitResult wait(int signals, int deadline) { |
33 List result = _MojoHandleNatives.wait(h, signals, deadline); | 33 List result = _MojoHandleNatives.wait(h, signals, deadline); |
34 return new MojoWaitResult(new MojoResult(result[0]), result[1]); | 34 return new MojoWaitResult(new MojoResult(result[0]), result[1]); |
35 } | 35 } |
36 | 36 |
37 bool _ready(int signal) { | 37 bool _ready(MojoHandleSignals signal) { |
38 MojoWaitResult res = wait(signal, 0); | 38 MojoWaitResult mwr = wait(signal.value, 0); |
39 switch (res.result) { | 39 switch (mwr.result) { |
40 case MojoResult.OK: | 40 case MojoResult.OK: |
41 return true; | 41 return true; |
42 case MojoResult.DEADLINE_EXCEEDED: | 42 case MojoResult.DEADLINE_EXCEEDED: |
43 case MojoResult.CANCELLED: | 43 case MojoResult.CANCELLED: |
44 case MojoResult.INVALID_ARGUMENT: | 44 case MojoResult.INVALID_ARGUMENT: |
45 case MojoResult.FAILED_PRECONDITION: | 45 case MojoResult.FAILED_PRECONDITION: |
46 return false; | 46 return false; |
47 default: | 47 default: |
48 // Should be unreachable. | 48 // Should be unreachable. |
49 throw new Exception("Unreachable"); | 49 throw "Unexpected result $res for wait on $h"; |
50 } | 50 } |
51 } | 51 } |
52 | 52 |
53 bool readyRead() => _ready(MojoHandleSignals.READABLE); | 53 bool get readyRead => _ready(MojoHandleSignals.READABLE); |
54 bool readyWrite() => _ready(MojoHandleSignals.WRITABLE); | 54 bool get readyWrite => _ready(MojoHandleSignals.WRITABLE); |
55 | 55 |
56 static MojoWaitManyResult waitMany(List<int> handles, | 56 static MojoWaitManyResult waitMany( |
57 List<int> signals, | 57 List<int> handles, List<int> signals, int deadline) { |
58 int deadline) { | 58 List result = _MojoHandleNatives.waitMany(handles, signals, deadline); |
59 List result = _MojoHandleNatives.waitMany( | |
60 handles, signals, deadline); | |
61 | |
62 return new MojoWaitManyResult( | 59 return new MojoWaitManyResult( |
63 new MojoResult(result[0]), result[1], result[2]); | 60 new MojoResult(result[0]), result[1], result[2]); |
64 } | 61 } |
65 | 62 |
66 static MojoResult register(MojoHandle handle) { | 63 static MojoResult register(MojoEventStream eventStream) { |
67 return new MojoResult(_MojoHandleNatives.register(handle)); | 64 return new MojoResult(_MojoHandleNatives.register(eventStream)); |
68 } | 65 } |
69 | 66 |
70 bool get isValid => (h != INVALID); | 67 bool get isValid => (h != INVALID); |
71 | 68 |
72 String toString() => "$h"; | 69 String toString() => "$h"; |
73 | 70 |
74 bool operator ==(RawMojoHandle other) { | 71 bool operator ==(MojoHandle other) { |
75 return h == other.h; | 72 return h == other.h; |
76 } | 73 } |
77 } | 74 } |
78 | 75 |
79 | 76 |
80 class MojoHandle extends Stream<int> { | 77 class MojoEventStream extends Stream<int> { |
81 // The underlying Mojo handle. | 78 // The underlying Mojo handle. |
82 RawMojoHandle _handle; | 79 MojoHandle _handle; |
83 | 80 |
84 // Providing our own stream controller allows us to take custom actions when | 81 // Providing our own stream controller allows us to take custom actions when |
85 // listeners pause/resume/etc. their StreamSubscription. | 82 // listeners pause/resume/etc. their StreamSubscription. |
86 StreamController _controller; | 83 StreamController _controller; |
87 | 84 |
88 // The send port that we give to the handle watcher to notify us of handle | 85 // The send port that we give to the handle watcher to notify us of handle |
89 // events. | 86 // events. |
90 SendPort _sendPort; | 87 SendPort _sendPort; |
91 | 88 |
92 // The receive port on which we listen and receive events from the handle | 89 // The receive port on which we listen and receive events from the handle |
93 // watcher. | 90 // watcher. |
94 ReceivePort _receivePort; | 91 ReceivePort _receivePort; |
95 | 92 |
96 // The signals on this handle that we're interested in. | 93 // The signals on this handle that we're interested in. |
97 int _signals; | 94 MojoHandleSignals _signals; |
98 | 95 |
99 // Whether the handle has been added to the handle watcher. | 96 // Whether listen has been called. |
100 bool _eventHandlerAdded; | 97 bool _isListening; |
101 | 98 |
102 MojoHandle(this._handle) : | 99 MojoEventStream(MojoHandle handle, |
103 _signals = MojoHandleSignals.READABLE, | 100 [MojoHandleSignals signals = MojoHandleSignals.READABLE]) : |
104 _eventHandlerAdded = false { | 101 _handle = handle, |
105 MojoResult result = RawMojoHandle.register(this); | 102 _signals = signals, |
| 103 _isListening = false { |
| 104 MojoResult result = MojoHandle.register(this); |
106 if (!result.isOk) { | 105 if (!result.isOk) { |
107 throw new Exception("Failed to register the MojoHandle"); | 106 throw "Failed to register the MojoHandle: $result."; |
108 } | 107 } |
109 } | 108 } |
110 | 109 |
111 void close() { | 110 void close() { |
112 if (_eventHandlerAdded) { | 111 if (_handle != null) { |
113 MojoHandleWatcher.close(_handle); | 112 MojoHandleWatcher.close(_handle); |
114 _eventHandlerAdded = false; | 113 _handle = null; |
115 } else { | |
116 // If we're not in the handle watcher, then close the handle manually. | |
117 _handle.close(); | |
118 } | 114 } |
119 if (_receivePort != null) { | 115 if (_receivePort != null) { |
120 _receivePort.close(); | 116 _receivePort.close(); |
| 117 _receivePort = null; |
121 } | 118 } |
122 } | 119 } |
123 | 120 |
124 // We wrap the callback provided by clients in listen() with some code to | 121 StreamSubscription<List<int>> listen( |
125 // handle adding and removing the handle to/from the handle watcher. Because | 122 void onData(List event), |
126 // the handle watcher removes this handle whenever it receives an event, | |
127 // we have to re-add it when the callback is finished. | |
128 Function _onDataClosure(origOnData) { | |
129 return ((int event) { | |
130 // The handle watcher removes this handle from its set on an event. | |
131 _eventHandlerAdded = false; | |
132 origOnData(event); | |
133 | |
134 // The callback could have closed the handle. If so, don't add it back to | |
135 // the MojoHandleWatcher. | |
136 if (_handle.isValid) { | |
137 assert(!_eventHandlerAdded); | |
138 var res = MojoHandleWatcher.add(_handle, _sendPort, _signals); | |
139 if (!res.isOk) { | |
140 throw new Exception("Failed to re-add handle: $res"); | |
141 } | |
142 _eventHandlerAdded = true; | |
143 } | |
144 }); | |
145 } | |
146 | |
147 StreamSubscription<int> listen( | |
148 void onData(int event), | |
149 {Function onError, void onDone(), bool cancelOnError}) { | 123 {Function onError, void onDone(), bool cancelOnError}) { |
| 124 if (_isListening) { |
| 125 throw "Listen has already been called: $_handle."; |
| 126 } |
150 _receivePort = new ReceivePort(); | 127 _receivePort = new ReceivePort(); |
151 _sendPort = _receivePort.sendPort; | 128 _sendPort = _receivePort.sendPort; |
152 _controller = new StreamController(sync: true, | 129 _controller = new StreamController(sync: true, |
153 onListen: _onSubscriptionStateChange, | 130 onListen: _onSubscriptionStateChange, |
154 onCancel: _onSubscriptionStateChange, | 131 onCancel: _onSubscriptionStateChange, |
155 onPause: _onPauseStateChange, | 132 onPause: _onPauseStateChange, |
156 onResume: _onPauseStateChange); | 133 onResume: _onPauseStateChange); |
157 _controller.addStream(_receivePort); | 134 _controller.addStream(_receivePort); |
158 | 135 |
159 assert(!_eventHandlerAdded); | 136 if (_signals != MojoHandleSignals.NONE) { |
160 var res = MojoHandleWatcher.add(_handle, _sendPort, _signals); | 137 var res = MojoHandleWatcher.add(_handle, _sendPort, _signals.value); |
161 if (!res.isOk) { | 138 if (!res.isOk) { |
162 throw new Exception("MojoHandleWatcher add failed: $res"); | 139 throw "MojoHandleWatcher add failed: $res"; |
| 140 } |
163 } | 141 } |
164 _eventHandlerAdded = true; | |
165 | 142 |
| 143 _isListening = true; |
166 return _controller.stream.listen( | 144 return _controller.stream.listen( |
167 _onDataClosure(onData), | 145 onData, |
168 onError: onError, | 146 onError: onError, |
169 onDone: onDone, | 147 onDone: onDone, |
170 cancelOnError: cancelOnError); | 148 cancelOnError: cancelOnError); |
171 } | 149 } |
172 | 150 |
173 bool writeEnabled() => MojoHandleSignals.isWritable(_signals); | 151 void enableSignals(MojoHandleSignals signals) { |
174 | 152 _signals = signals; |
175 void toggleWriteEvents() { | 153 if (_isListening) { |
176 _signals = MojoHandleSignals.toggleWrite(_signals); | 154 var res = MojoHandleWatcher.add(_handle, _sendPort, signals.value); |
177 if (_eventHandlerAdded) { | |
178 var res = MojoHandleWatcher.toggleWrite(_handle); | |
179 if (!res.isOk) { | 155 if (!res.isOk) { |
180 throw new Exception("MojoHandleWatcher failed to toggle write: $res"); | 156 throw "MojoHandleWatcher add failed: $res"; |
181 } | 157 } |
182 } | 158 } |
183 } | 159 } |
184 | 160 |
185 void enableWriteEvents() { | 161 void enableReadEvents() => enableSignals(MojoHandleSignals.READABLE); |
186 assert(!writeEnabled()); | 162 void enableWriteEvents() => enableSignals(MojoHandleSignals.WRITABLE); |
187 toggleWriteEvents(); | 163 void enableAllEvents() => enableSignals(MojoHandleSignals.READWRITE); |
188 } | |
189 | |
190 void disableWriteEvents() { | |
191 assert(writeEnabled()); | |
192 toggleWriteEvents(); | |
193 } | |
194 | 164 |
195 void _onSubscriptionStateChange() { | 165 void _onSubscriptionStateChange() { |
196 if (!_controller.hasListener) { | 166 if (!_controller.hasListener) { |
197 close(); | 167 close(); |
198 } | 168 } |
199 } | 169 } |
200 | 170 |
201 void _onPauseStateChange() { | 171 void _onPauseStateChange() { |
202 if (_controller.isPaused) { | 172 if (_controller.isPaused) { |
203 if (_eventHandlerAdded) { | 173 var res = MojoHandleWatcher.remove(_handle); |
204 var res = MojoHandleWatcher.remove(_handle); | 174 if (!res.isOk) { |
205 if (!res.isOk) { | 175 throw "MojoHandleWatcher add failed: $res"; |
206 throw new Exception("MojoHandleWatcher add failed: $res"); | |
207 } | |
208 _eventHandlerAdded = false; | |
209 } | 176 } |
210 } else { | 177 } else { |
211 if (!_eventHandlerAdded) { | 178 var res = MojoHandleWatcher.add(_handle, _sendPort, _signals.value); |
212 var res = MojoHandleWatcher.add(_handle, _sendPort, _signals); | 179 if (!res.isOk) { |
213 if (!res.isOk) { | 180 throw "MojoHandleWatcher add failed: $res"; |
214 throw new Exception("MojoHandleWatcher add failed: $res"); | |
215 } | |
216 _eventHandlerAdded = true; | |
217 } | 181 } |
218 } | 182 } |
219 } | 183 } |
220 | 184 |
| 185 bool get readyRead => _handle.readyRead; |
| 186 bool get readyWrite => _handle.readyWrite; |
| 187 |
221 String toString() => "$_handle"; | 188 String toString() => "$_handle"; |
222 } | 189 } |
OLD | NEW |