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