OLD | NEW |
(Empty) | |
| 1 # ![Mojo Graphic](https://goo.gl/6CdlbH) Mojo C++ System API |
| 2 This document is a subset of the [Mojo documentation](/mojo). |
| 3 |
| 4 [TOC] |
| 5 |
| 6 ## Overview |
| 7 The Mojo C++ System API provides a convenient set of helper classes and |
| 8 functions for working with Mojo primitives. Unlike the low-level |
| 9 [C API](/mojo/public/c/system) (upon which this is built) this library takes |
| 10 advantage of C++ language features and common STL and `//base` types to provide |
| 11 a slightly more idiomatic interface to the Mojo system layer, making it |
| 12 generally easier to use. |
| 13 |
| 14 This document provides a brief guide to API usage with example code snippets. |
| 15 For a detailed API references please consult the headers in |
| 16 [//mojo/public/cpp/system](https://cs.chromium.org/chromium/src/mojo/public/cpp/
system/). |
| 17 |
| 18 Note that all API symbols referenced in this document are implicitly in the |
| 19 top-level `mojo` namespace. |
| 20 |
| 21 ## Scoped, Typed Handles |
| 22 |
| 23 All types of Mojo handles in the C API are simply opaque, integral `MojoHandle` |
| 24 values. The C++ API has more strongly typed wrappers defined for different |
| 25 handle types: `MessagePipeHandle`, `SharedBufferHandle`, |
| 26 `DataPipeConsumerHandle`, `DataPipeProducerHandle`, and `WatcherHandle`. |
| 27 |
| 28 Each of these also has a corresponding, move-only, scoped type for safer usage: |
| 29 `ScopedMessagePipeHandle`, `ScopedSharedBufferHandle`, and so on. When a scoped |
| 30 handle type is destroyed, its handle is automatically closed via `MojoClose`. |
| 31 When working with raw handles you should **always** prefer to use one of the |
| 32 scoped types for ownership. |
| 33 |
| 34 Similar to `std::unique_ptr`, scoped handle types expose a `get()` method to get |
| 35 at the underlying unscoped handle type as well as the `->` operator to |
| 36 dereference the scoper and make calls directly on the underlying handle type. |
| 37 |
| 38 ## Message Pipes |
| 39 |
| 40 There are two ways to create a new message pipe using the C++ API. You may |
| 41 construct a `MessagePipe` object: |
| 42 |
| 43 ``` cpp |
| 44 mojo::MessagePipe pipe; |
| 45 |
| 46 // NOTE: Because pipes are bi-directional there is no implicit semantic |
| 47 // difference between |handle0| or |handle1| here. They're just two ends of a |
| 48 // pipe. The choice to treat one as a "client" and one as a "server" is entirely |
| 49 // a the API user's decision. |
| 50 mojo::ScopedMessagePipeHandle client = std::move(pipe.handle0); |
| 51 mojo::ScopedMessagePipeHandle server = std::move(pipe.handle1); |
| 52 ``` |
| 53 |
| 54 or you may call `CreateMessagePipe`: |
| 55 |
| 56 ``` cpp |
| 57 mojo::ScopedMessagePipeHandle client; |
| 58 mojo::ScopedMessagePipeHandle server; |
| 59 mojo::CreateMessagePipe(nullptr, &client, &server); |
| 60 ``` |
| 61 |
| 62 There are also some helper functions for constructing message objects and |
| 63 reading/writing them on pipes using the library's more strongly-typed C++ |
| 64 handles: |
| 65 |
| 66 ``` cpp |
| 67 mojo::ScopedMessageHandle message; |
| 68 mojo::AllocMessage(6, nullptr, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE, &message); |
| 69 |
| 70 void *buffer; |
| 71 mojo::GetMessageBuffer(message.get(), &buffer); |
| 72 |
| 73 const std::string kMessage = "hello"; |
| 74 std::copy(kMessage.begin(), kMessage.end(), static_cast<char*>(buffer)); |
| 75 |
| 76 mojo::WriteMessageNew(client.get(), std::move(message), |
| 77 MOJO_WRITE_MESSAGE_FLAG_NONE); |
| 78 |
| 79 // Some time later... |
| 80 |
| 81 mojo::ScopedMessageHandle received_message; |
| 82 uint32_t num_bytes; |
| 83 mojo::ReadMessageNew(server.get(), &received_message, &num_bytes, nullptr, |
| 84 nullptr, MOJO_READ_MESSAGE_FLAG_NONE); |
| 85 ``` |
| 86 |
| 87 See [message_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system
/message_pipe.h) |
| 88 for detailed C++ message pipe API documentation. |
| 89 |
| 90 ## Data Pipes |
| 91 |
| 92 Similar to [Message Pipes](#Message-Pipes), the C++ library has some simple |
| 93 helpers for more strongly-typed data pipe usage: |
| 94 |
| 95 ``` cpp |
| 96 mojo::DataPipe pipe; |
| 97 mojo::ScopedDataPipeProducerHandle producer = std::move(pipe.producer); |
| 98 mojo::ScopedDataPipeConsumerHandle consumer = std::move(pipe.consumer); |
| 99 |
| 100 // Or alternatively: |
| 101 mojo::ScopedDataPipeProducerHandle producer; |
| 102 mojo::ScopedDataPipeConsumerHandle consumer; |
| 103 mojo::CreateDataPipe(null, &producer, &consumer); |
| 104 ``` |
| 105 |
| 106 // Reads from a data pipe. See |MojoReadData()| for complete documentation. |
| 107 inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, |
| 108 void* elements, |
| 109 uint32_t* num_bytes, |
| 110 MojoReadDataFlags flags) { |
| 111 return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags); |
| 112 } |
| 113 |
| 114 // Begins a two-phase read |
| 115 C++ helpers which correspond directly to the |
| 116 [Data Pipe C API](/mojo/public/c/system#Data-Pipes) for immediate and two-phase |
| 117 I/O are provided as well. For example: |
| 118 |
| 119 ``` cpp |
| 120 uint32_t num_bytes = 7; |
| 121 mojo::WriteDataRaw(producer.get(), "hihihi", |
| 122 &num_bytes, MOJO_WRITE_DATA_FLAG_NONE); |
| 123 |
| 124 // Some time later... |
| 125 |
| 126 char buffer[64]; |
| 127 uint32_t num_bytes = 64; |
| 128 mojo::ReadDataRaw(consumer.get(), buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); |
| 129 ``` |
| 130 |
| 131 See [data_pipe.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/da
ta_pipe.h) |
| 132 for detailed C++ data pipe API documentation. |
| 133 |
| 134 ## Shared Buffers |
| 135 |
| 136 A new shared buffers can be allocated like so: |
| 137 |
| 138 ``` cpp |
| 139 mojo::ScopedSharedBufferHandle buffer = |
| 140 mojo::ScopedSharedBufferHandle::Create(4096); |
| 141 ``` |
| 142 |
| 143 This new handle can be cloned arbitrarily many times by using the underlying |
| 144 handle's `Clone` method: |
| 145 |
| 146 ``` cpp |
| 147 mojo::ScopedSharedBufferHandle another_handle = buffer->Clone(); |
| 148 mojo::ScopedSharedBufferHandle read_only_handle = |
| 149 buffer->Clone(mojo::SharedBufferHandle::AccessMode::READ_ONLY); |
| 150 ``` |
| 151 |
| 152 And finally the library also provides a scoper for mapping the shared buffer's |
| 153 memory: |
| 154 |
| 155 ``` cpp |
| 156 mojo::ScopedSharedBufferMapping mapping = buffer->Map(64); |
| 157 static_cast<int*>(mapping.get()) = 42; |
| 158 |
| 159 mojo::ScopedSharedBufferMapping another_mapping = buffer->MapAtOffset(64, 4); |
| 160 static_cast<int*>(mapping.get()) = 43; |
| 161 ``` |
| 162 |
| 163 When `mapping` and `another_mapping` are destroyed, they automatically unmap |
| 164 their respective memory regions. |
| 165 |
| 166 See [buffer.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/buffe
r.h) |
| 167 for detailed C++ shared buffer API documentation. |
| 168 |
| 169 ## Native Platform Handles (File Descriptors, Windows Handles, *etc.*) |
| 170 |
| 171 The C++ library provides several helpers for wrapping system handle types. |
| 172 These are specifically useful when working with a few `//base` types, namely |
| 173 `base::PlatformFile` and `base::SharedMemoryHandle`. See |
| 174 [platform_handle.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/
platform_handle.h) |
| 175 for detailed C++ platform handle API documentation. |
| 176 |
| 177 ## Signals & Watchers |
| 178 |
| 179 For an introduction to the concepts of handle signals and watchers, check out |
| 180 the C API's documentation on [Signals & Watchers](/mojo/public/c/system#Signals-
Watchers). |
| 181 |
| 182 ### Querying Signals |
| 183 |
| 184 Any C++ handle type's last known signaling state can be queried by calling the |
| 185 `QuerySignalsState` method on the handle: |
| 186 |
| 187 ``` cpp |
| 188 mojo::MessagePipe message_pipe; |
| 189 mojo::DataPipe data_pipe; |
| 190 mojo::HandleSignalsState a = message_pipe.handle0->QuerySignalsState(); |
| 191 mojo::HandleSignalsState b = data_pipe.consumer->QuerySignalsState(); |
| 192 ``` |
| 193 |
| 194 The `HandleSignalsState` is a thin wrapper interface around the C API's |
| 195 `MojoHandleSignalsState` structure with convenient accessors for testing |
| 196 the signal bitmasks. Whereas when using the C API you might write: |
| 197 |
| 198 ``` c |
| 199 struct MojoHandleSignalsState state; |
| 200 MojoQueryHandleSignalsState(handle0, &state); |
| 201 if (state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) { |
| 202 // ... |
| 203 } |
| 204 ``` |
| 205 |
| 206 the C++ API equivalent would be: |
| 207 |
| 208 ``` cpp |
| 209 if (message_pipe.handle0->QuerySignalsState().readable()) { |
| 210 // ... |
| 211 } |
| 212 ``` |
| 213 |
| 214 ### Watching Handles |
| 215 |
| 216 The [`mojo::SimpleWatcher`](https://cs.chromium.org/chromium/src/mojo/public/cpp
/system/simple_watcher.h) |
| 217 class serves as a convenient helper for using the [low-level watcher API](/mojo/
public/c/system#Signals-Watchers) |
| 218 to watch a handle for signaling state changes. A `SimpleWatcher` is bound to a |
| 219 single thread and always dispatches its notifications on a |
| 220 `base::SingleThreadTaskRunner`. |
| 221 |
| 222 `SimpleWatcher` has two possible modes of operation, selected at construction |
| 223 time by the `mojo::SimpleWatcher::ArmingPolicy` enum: |
| 224 |
| 225 * `MANUAL` mode requires the user to manually call `Arm` and/or `ArmOrNotify` |
| 226 before any notifications will fire regarding the state of the watched handle. |
| 227 Every time the notification callback is run, the `SimpleWatcher` must be |
| 228 rearmed again before the next one can fire. See |
| 229 [Arming a Watcher](/mojo/public/c/system#Arming-a-Watcher) and the |
| 230 documentation in `SimpleWatcher`'s header. |
| 231 |
| 232 * `AUTOMATIC` mode ensures that the `SimpleWatcher` always either is armed or |
| 233 has a pending notification task queued for execution. |
| 234 |
| 235 `AUTOMATIC` mode is more convenient but can result in redundant notification |
| 236 tasks, especially if the provided callback does not make a strong effort to |
| 237 return the watched handle to an uninteresting signaling state (by *e.g.*, |
| 238 reading all its available messages when notified of readability.) |
| 239 |
| 240 Example usage: |
| 241 |
| 242 ``` cpp |
| 243 class PipeReader { |
| 244 public: |
| 245 PipeReader(mojo::ScopedMessagePipeHandle pipe) |
| 246 : pipe_(std::move(pipe)), |
| 247 watcher_(mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC) { |
| 248 // NOTE: base::Unretained is safe because the callback can never be run |
| 249 // after SimpleWatcher destruction. |
| 250 watcher_.Watch(pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, |
| 251 base::Bind(&PipeReader::OnReadable, base::Unretained(this))); |
| 252 } |
| 253 |
| 254 ~PipeReader() {} |
| 255 |
| 256 private: |
| 257 void OnReadable(MojoResult result) { |
| 258 while (result == MOJO_RESULT_OK) { |
| 259 mojo::ScopedMessageHandle message; |
| 260 uint32_t num_bytes; |
| 261 result = mojo::ReadMessageNew(pipe_.get(), &message, &num_bytes, nullptr, |
| 262 nullptr, MOJO_READ_MESSAGE_FLAG_NONE); |
| 263 DCHECK_EQ(result, MOJO_RESULT_OK); |
| 264 messages_.emplace_back(std::move(message)); |
| 265 } |
| 266 } |
| 267 |
| 268 mojo::ScopedMessagePipeHandle pipe_; |
| 269 mojo::SimpleWatcher watcher_; |
| 270 std::vector<mojo::ScopedMessageHandle> messages_; |
| 271 }; |
| 272 |
| 273 mojo::MessagePipe pipe; |
| 274 PipeReader reader(std::move(pipe.handle0)); |
| 275 |
| 276 // Written messages will asynchronously end up in |reader.messages_|. |
| 277 WriteABunchOfStuff(pipe.handle1.get()); |
| 278 ``` |
| 279 |
| 280 ## Synchronous Waiting |
| 281 |
| 282 The C++ System API defines some utilities to block a calling thread while |
| 283 waiting for one or more handles to change signaling state in an interesting way. |
| 284 These threads combine usage of the [low-level Watcher API](/mojo/public/c/system
#Signals-Watchers) |
| 285 with common synchronization primitives (namely `base::WaitableEvent`.) |
| 286 |
| 287 While these API features should be used sparingly, they are sometimes necessary. |
| 288 |
| 289 See the documentation in |
| 290 [wait.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wait.h) |
| 291 and [wait_set.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wai
t_set.h) |
| 292 for a more detailed API reference. |
| 293 |
| 294 ### Waiting On a Single Handle |
| 295 |
| 296 The `mojo::Wait` function simply blocks the calling thread until a given signal |
| 297 mask is either partially satisfied or fully unsatisfiable on a given handle. |
| 298 |
| 299 ``` cpp |
| 300 mojo::MessagePipe pipe; |
| 301 mojo::WriteMessageRaw(pipe.handle0.get(), "hey", 3, nullptr, nullptr, |
| 302 MOJO_WRITE_MESSAGE_FLAG_NONE); |
| 303 MojoResult result = mojo::Wait(pipe.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE); |
| 304 DCHECK_EQ(result, MOJO_RESULT_OK); |
| 305 |
| 306 // Guaranteed to succeed because we know |handle1| is readable now. |
| 307 mojo::ScopedMessageHandle message; |
| 308 uint32_t num_bytes; |
| 309 mojo::ReadMessageNew(pipe.handle1.get(), &num_bytes, nullptr, nullptr, |
| 310 MOJO_READ_MESSAGE_FLAG_NONE); |
| 311 ``` |
| 312 |
| 313 `mojo::Wait` is most typically useful in limited testing scenarios. |
| 314 |
| 315 ### Waiting On Multiple Handles |
| 316 |
| 317 `mojo::WaitMany` provides a simple API to wait on multiple handles |
| 318 simultaneously, returning when any handle's given signal mask is either |
| 319 partially satisfied or fully unsatisfiable. |
| 320 |
| 321 ``` cpp |
| 322 mojo::MessagePipe a, b; |
| 323 GoDoSomethingWithPipes(std:move(a.handle1), std::move(b.handle1)); |
| 324 |
| 325 mojo::MessagePipeHandle handles[2] = {a.handle0.get(), b.handle0.get()}; |
| 326 MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE, |
| 327 MOJO_HANDLE_SIGNAL_READABLE}; |
| 328 size_t ready_index; |
| 329 MojoResult result = mojo::WaitMany(handles, signals, 2, &ready_index); |
| 330 if (ready_index == 0) { |
| 331 // a.handle0 was ready. |
| 332 } else { |
| 333 // b.handle0 was ready. |
| 334 } |
| 335 ``` |
| 336 |
| 337 Similar to `mojo::Wait`, `mojo::WaitMany` is primarily useful in testing. When |
| 338 waiting on multiple handles in production code, you should almost always instead
|
| 339 use a more efficient and more flexible `mojo::WaitSet` as described in the next |
| 340 section. |
| 341 |
| 342 ### Waiting On Handles and Events Simultaneously |
| 343 |
| 344 Typically when waiting on one or more handles to signal, the set of handles and |
| 345 conditions being waited upon do not change much between consecutive blocking |
| 346 waits. It's also often useful to be able to interrupt the blocking operation |
| 347 as efficiently as possible. |
| 348 |
| 349 [`mojo::WaitSet`](https://cs.chromium.org/chromium/src/mojo/public/cpp/system/wa
it_set.h) |
| 350 is designed with these conditions in mind. A `WaitSet` maintains a persistent |
| 351 set of (not-owned) Mojo handles and `base::WaitableEvent`s, which may be |
| 352 explicitly added to or removed from the set at any time. |
| 353 |
| 354 The `WaitSet` may be waited upon repeatedly, each time blocking the calling |
| 355 thread until either one of the handles attains an interesting signaling state or |
| 356 one of the events is signaled. For example let's suppose we want to wait up to 5 |
| 357 seconds for either one of two handles to become readable: |
| 358 |
| 359 ``` cpp |
| 360 base::WaitableEvent timeout_event( |
| 361 base::WaitableEvent::ResetPolicy::MANUAL, |
| 362 base::WaitableEvent::InitialState::NOT_SIGNALED); |
| 363 mojo::MessagePipe a, b; |
| 364 |
| 365 GoDoStuffWithPipes(std::move(a.handle1), std::move(b.handle1)); |
| 366 |
| 367 mojo::WaitSet wait_set; |
| 368 wait_set.AddHandle(a.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE); |
| 369 wait_set.AddHandle(b.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE); |
| 370 wait_set.AddEvent(&timeout_event); |
| 371 |
| 372 // Ensure the Wait() lasts no more than 5 seconds. |
| 373 bg_thread->task_runner()->PostDelayedTask( |
| 374 FROM_HERE, |
| 375 base::Bind([](base::WaitableEvent* e) { e->Signal(); }, &timeout_event); |
| 376 base::TimeDelta::FromSeconds(5)); |
| 377 |
| 378 base::WaitableEvent* ready_event = nullptr; |
| 379 size_t num_ready_handles = 1; |
| 380 mojo::Handle ready_handle; |
| 381 MojoResult ready_result; |
| 382 wait_set.Wait(&ready_event, &num_ready_handles, &ready_handle, &ready_result); |
| 383 |
| 384 // The apex of thread-safety. |
| 385 bg_thread->Stop(); |
| 386 |
| 387 if (ready_event) { |
| 388 // The event signaled... |
| 389 } |
| 390 |
| 391 if (num_ready_handles > 0) { |
| 392 // At least one of the handles signaled... |
| 393 // NOTE: This and the above condition are not mutually exclusive. If handle |
| 394 // signaling races with timeout, both things might be true. |
| 395 } |
| 396 ``` |
OLD | NEW |