Flutter Engine
The Flutter Engine
eventhandler.h
Go to the documentation of this file.
1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
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.
4
5#ifndef RUNTIME_BIN_EVENTHANDLER_H_
6#define RUNTIME_BIN_EVENTHANDLER_H_
7
8#include "bin/builtin.h"
9#include "bin/dartutils.h"
10#include "bin/isolate_data.h"
11
12#include "platform/hashmap.h"
14
15namespace dart {
16namespace bin {
17
18// Flags used to provide information and actions to the eventhandler
19// when sending a message about a file descriptor. These flags should
20// be kept in sync with the constants in socket_patch.dart. For more
21// information see the comments in socket_patch.dart
34 kPipe = 17,
36};
37
38// clang-format off
39#define COMMAND_MASK ((1 << kCloseCommand) | \
40 (1 << kShutdownReadCommand) | \
41 (1 << kShutdownWriteCommand) | \
42 (1 << kReturnTokenCommand) | \
43 (1 << kSetEventMaskCommand))
44#define EVENT_MASK ((1 << kInEvent) | \
45 (1 << kOutEvent) | \
46 (1 << kErrorEvent) | \
47 (1 << kCloseEvent) | \
48 (1 << kDestroyedEvent))
49#define IS_COMMAND(data, command_bit) \
50 ((data & COMMAND_MASK) == (1 << command_bit)) // NOLINT
51#define IS_EVENT(data, event_bit) \
52 ((data & EVENT_MASK) == (1 << event_bit)) // NOLINT
53#define IS_IO_EVENT(data) \
54 ((data & (1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) != 0 && \
55 (data & ~(1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) == 0)
56#define IS_LISTENING_SOCKET(data) \
57 ((data & (1 << kListeningSocket)) != 0) // NOLINT
58#define IS_SIGNAL_SOCKET(data) \
59 ((data & (1 << kSignalSocket)) != 0) // NOLINT
60#define TOKEN_COUNT(data) (data & ((1 << kCloseCommand) - 1))
61// clang-format on
62
64 public:
66
68 while (HasTimeout())
70 }
71
72 bool HasTimeout() const { return !timeouts_.IsEmpty(); }
73
74 int64_t CurrentTimeout() const {
75 ASSERT(!timeouts_.IsEmpty());
76 return timeouts_.Minimum().priority;
77 }
78
80 ASSERT(!timeouts_.IsEmpty());
81 return timeouts_.Minimum().value;
82 }
83
84 void RemoveCurrent() { timeouts_.RemoveMinimum(); }
85
87 if (timeout < 0) {
88 timeouts_.RemoveByValue(port);
89 } else {
91 }
92 }
93
94 private:
96
97 DISALLOW_COPY_AND_ASSIGN(TimeoutQueue);
98};
99
101 public:
102 intptr_t id;
104 int64_t data;
105};
106
107static constexpr intptr_t kInterruptMessageSize = sizeof(InterruptMessage);
108static constexpr intptr_t kInfinityTimeout = -1;
109static constexpr intptr_t kTimerId = -1;
110static constexpr intptr_t kShutdownId = -2;
111
112template <typename T>
114 public:
115 CircularLinkedList() : head_(nullptr) {}
116
117 typedef void (*ClearFun)(void* value);
118
119 // Returns true if the list was empty.
120 bool Add(T t) {
121 Entry* e = new Entry(t);
122 if (head_ == nullptr) {
123 // Empty list, make e head, and point to itself.
124 e->next_ = e;
125 e->prev_ = e;
126 head_ = e;
127 return true;
128 } else {
129 // Insert e as the last element in the list.
130 e->prev_ = head_->prev_;
131 e->next_ = head_;
132 e->prev_->next_ = e;
133 head_->prev_ = e;
134 return false;
135 }
136 }
137
138 void RemoveHead(ClearFun clear = nullptr) {
139 ASSERT(head_ != nullptr);
140
141 Entry* e = head_;
142 if (e->next_ == e) {
143 head_ = nullptr;
144 } else {
145 e->prev_->next_ = e->next_;
146 e->next_->prev_ = e->prev_;
147 head_ = e->next_;
148 }
149 if (clear != nullptr) {
150 clear(reinterpret_cast<void*>(e->t));
151 }
152 delete e;
153 }
154
155 void Remove(T item) {
156 if (head_ == nullptr) {
157 return;
158 } else if (head_ == head_->next_) {
159 if (head_->t == item) {
160 delete head_;
161 head_ = nullptr;
162 return;
163 }
164 } else {
165 Entry* current = head_;
166 do {
167 if (current->t == item) {
168 Entry* next = current->next_;
169 Entry* prev = current->prev_;
170 prev->next_ = next;
171 next->prev_ = prev;
172
173 if (current == head_) {
174 head_ = head_->next_;
175 }
176
177 delete current;
178 return;
179 }
180 current = current->next_;
181 } while (current != head_);
182 }
183 }
184
185 void RemoveAll(ClearFun clear = nullptr) {
186 while (HasHead()) {
187 RemoveHead(clear);
188 }
189 }
190
191 T head() const { return head_->t; }
192
193 bool HasHead() const { return head_ != nullptr; }
194
195 void Rotate() {
196 if (head_ != nullptr) {
197 ASSERT(head_->next_ != nullptr);
198 head_ = head_->next_;
199 }
200 }
201
202 private:
203 struct Entry {
204 explicit Entry(const T& t) : t(t), next_(nullptr), prev_(nullptr) {}
205 const T t;
206 Entry* next_;
207 Entry* prev_;
208 };
209
210 Entry* head_;
211
212 DISALLOW_COPY_AND_ASSIGN(CircularLinkedList);
213};
214
216 public:
217 explicit DescriptorInfoBase(intptr_t fd) : fd_(fd) { ASSERT(fd_ != -1); }
218
220
221 // The OS descriptor.
222 intptr_t fd() { return fd_; }
223
224 // Whether this descriptor refers to an underlying listening OS socket.
225 virtual bool IsListeningSocket() const = 0;
226
227 // Inserts or updates a new Dart_Port which is interested in events specified
228 // in `mask`.
229 virtual void SetPortAndMask(Dart_Port port, intptr_t mask) = 0;
230
231 // Removes a port from the interested listeners.
232 virtual void RemovePort(Dart_Port port) = 0;
233
234 // Removes all ports from the interested listeners.
235 virtual void RemoveAllPorts() = 0;
236
237 // Returns a port to which `events_ready` can be sent to. It will also
238 // decrease the token count by 1 for this port.
239 virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) = 0;
240
241 // Will post `data` to all known Dart_Ports. It will also decrease the token
242 // count by 1 for all ports.
243 virtual void NotifyAllDartPorts(uintptr_t events) = 0;
244
245 // Returns `count` tokens for the given port.
246 virtual void ReturnTokens(Dart_Port port, int count) = 0;
247
248 // Returns the union of event masks of all ports. If a port has a non-positive
249 // token count it's mask is assumed to be 0.
250 virtual intptr_t Mask() = 0;
251
252 // Closes this descriptor.
253 virtual void Close() = 0;
254
255 protected:
256 intptr_t fd_;
257
258 private:
259 DISALLOW_COPY_AND_ASSIGN(DescriptorInfoBase);
260};
261
262// Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on
263// windows) which is connected to a single Dart_Port.
264//
265// Subclasses of this class can be e.g. connected tcp sockets.
266template <typename DI>
267class DescriptorInfoSingleMixin : public DI {
268 private:
269 static constexpr int kTokenCount = 16;
270
271 public:
272 DescriptorInfoSingleMixin(intptr_t fd, bool disable_tokens)
273 : DI(fd),
274 port_(0),
275 tokens_(kTokenCount),
276 mask_(0),
277 disable_tokens_(disable_tokens) {}
278
280
281 virtual bool IsListeningSocket() const { return false; }
282
283 virtual void SetPortAndMask(Dart_Port port, intptr_t mask) {
284 ASSERT(port_ == 0 || port == port_);
285 port_ = port;
286 mask_ = mask;
287 }
288
289 virtual void RemovePort(Dart_Port port) {
290 // TODO(dart:io): Find out where we call RemovePort() with the invalid
291 // port. Afterwards remove the part in the ASSERT here.
292 ASSERT(port_ == 0 || port_ == port);
293 port_ = 0;
294 mask_ = 0;
295 }
296
297 virtual void RemoveAllPorts() {
298 port_ = 0;
299 mask_ = 0;
300 }
301
302 virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) {
303 ASSERT(IS_IO_EVENT(events_ready) ||
304 IS_EVENT(events_ready, kDestroyedEvent));
305 if (!disable_tokens_) {
306 tokens_--;
307 }
308 return port_;
309 }
310
311 virtual void NotifyAllDartPorts(uintptr_t events) {
312 // Unexpected close, asynchronous destroy or error events are the only
313 // ones we broadcast to all listeners.
314 ASSERT(IS_EVENT(events, kCloseEvent) || IS_EVENT(events, kErrorEvent) ||
315 IS_EVENT(events, kDestroyedEvent));
316
317 if (port_ != 0) {
318 DartUtils::PostInt32(port_, events);
319 }
320 if (!disable_tokens_) {
321 tokens_--;
322 }
323 }
324
325 virtual void ReturnTokens(Dart_Port port, int count) {
326 ASSERT(port_ == port);
327 if (!disable_tokens_) {
328 tokens_ += count;
329 }
330 ASSERT(tokens_ <= kTokenCount);
331 }
332
333 virtual intptr_t Mask() {
334 if (tokens_ <= 0) {
335 return 0;
336 }
337 return mask_;
338 }
339
340 virtual void Close() { DI::Close(); }
341
342 private:
343 Dart_Port port_;
344 int tokens_;
345 intptr_t mask_;
346 bool disable_tokens_;
347
348 DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingleMixin);
349};
350
351// Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on
352// windows) which is connected to multiple Dart_Port's.
353//
354// Subclasses of this class can be e.g. a listening socket which multiple
355// isolates are listening on.
356template <typename DI>
358 private:
359 static constexpr int kTokenCount = 4;
360
361 static bool SamePortValue(void* key1, void* key2) {
362 return reinterpret_cast<Dart_Port>(key1) ==
363 reinterpret_cast<Dart_Port>(key2);
364 }
365
366 static uint32_t GetHashmapHashFromPort(Dart_Port port) {
367 return static_cast<uint32_t>(port & 0xFFFFFFFF);
368 }
369
370 static void* GetHashmapKeyFromPort(Dart_Port port) {
371 return reinterpret_cast<void*>(port);
372 }
373
374 static bool IsReadingMask(intptr_t mask) {
375 if (mask == (1 << kInEvent)) {
376 return true;
377 } else {
378 ASSERT(mask == 0);
379 return false;
380 }
381 }
382
383 struct PortEntry {
384 Dart_Port dart_port;
385 intptr_t is_reading;
386 intptr_t token_count;
387
388 bool IsReady() { return token_count > 0 && is_reading != 0; }
389 };
390
391 public:
392 DescriptorInfoMultipleMixin(intptr_t fd, bool disable_tokens)
393 : DI(fd),
394 tokens_map_(&SamePortValue, kTokenCount),
395 disable_tokens_(disable_tokens) {}
396
398
399 virtual bool IsListeningSocket() const { return true; }
400
401 virtual void SetPortAndMask(Dart_Port port, intptr_t mask) {
402 SimpleHashMap::Entry* entry = tokens_map_.Lookup(
403 GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), true);
404 PortEntry* pentry;
405 if (entry->value == nullptr) {
406 pentry = new PortEntry();
407 pentry->dart_port = port;
408 pentry->token_count = kTokenCount;
409 pentry->is_reading = IsReadingMask(mask);
410 entry->value = reinterpret_cast<void*>(pentry);
411
412 if (pentry->IsReady()) {
413 active_readers_.Add(pentry);
414 }
415 } else {
416 pentry = reinterpret_cast<PortEntry*>(entry->value);
417 bool was_ready = pentry->IsReady();
418 pentry->is_reading = IsReadingMask(mask);
419 bool is_ready = pentry->IsReady();
420
421 if (was_ready && !is_ready) {
422 active_readers_.Remove(pentry);
423 } else if (!was_ready && is_ready) {
424 active_readers_.Add(pentry);
425 }
426 }
427
428#ifdef DEBUG
429 // To ensure that all readers are ready.
430 int ready_count = 0;
431
432 if (active_readers_.HasHead()) {
433 PortEntry* root = reinterpret_cast<PortEntry*>(active_readers_.head());
434 PortEntry* current = root;
435 do {
436 ASSERT(current->IsReady());
437 ready_count++;
438 active_readers_.Rotate();
439 current = active_readers_.head();
440 } while (current != root);
441 }
442
443 for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr;
444 entry = tokens_map_.Next(entry)) {
445 PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
446 if (pentry->IsReady()) {
447 ready_count--;
448 }
449 }
450 // Ensure all ready items are in `active_readers_`.
451 ASSERT(ready_count == 0);
452#endif
453 }
454
455 virtual void RemovePort(Dart_Port port) {
456 SimpleHashMap::Entry* entry = tokens_map_.Lookup(
457 GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
458 if (entry != nullptr) {
459 PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
460 if (pentry->IsReady()) {
461 active_readers_.Remove(pentry);
462 }
463 tokens_map_.Remove(GetHashmapKeyFromPort(port),
464 GetHashmapHashFromPort(port));
465 delete pentry;
466 } else {
467 // NOTE: This is a listening socket which has been immediately closed.
468 //
469 // If a listening socket is not listened on, the event handler does not
470 // know about it beforehand. So the first time the event handler knows
471 // about it, is when it is supposed to be closed. We therefore do nothing
472 // here.
473 //
474 // But whether to close it, depends on whether other isolates have it open
475 // as well or not.
476 }
477 }
478
479 virtual void RemoveAllPorts() {
480 for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr;
481 entry = tokens_map_.Next(entry)) {
482 PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
483 entry->value = nullptr;
484 active_readers_.Remove(pentry);
485 delete pentry;
486 }
487 tokens_map_.Clear();
488 active_readers_.RemoveAll(DeletePortEntry);
489 }
490
491 virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) {
492 // We're only sending `kInEvents` if there are multiple listeners (which is
493 // listening socktes).
494 ASSERT(IS_EVENT(events_ready, kInEvent) ||
495 IS_EVENT(events_ready, kDestroyedEvent));
496
497 if (active_readers_.HasHead()) {
498 PortEntry* pentry = reinterpret_cast<PortEntry*>(active_readers_.head());
499
500 // Update token count.
501 if (!disable_tokens_) {
502 pentry->token_count--;
503 }
504 if (pentry->token_count <= 0) {
505 active_readers_.RemoveHead();
506 } else {
507 active_readers_.Rotate();
508 }
509
510 return pentry->dart_port;
511 }
512 return 0;
513 }
514
515 virtual void NotifyAllDartPorts(uintptr_t events) {
516 // Unexpected close, asynchronous destroy or error events are the only
517 // ones we broadcast to all listeners.
518 ASSERT(IS_EVENT(events, kCloseEvent) || IS_EVENT(events, kErrorEvent) ||
519 IS_EVENT(events, kDestroyedEvent));
520
521 for (SimpleHashMap::Entry* entry = tokens_map_.Start(); entry != nullptr;
522 entry = tokens_map_.Next(entry)) {
523 PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
524 DartUtils::PostInt32(pentry->dart_port, events);
525
526 // Update token count.
527 bool was_ready = pentry->IsReady();
528 if (!disable_tokens_) {
529 pentry->token_count--;
530 }
531
532 if (was_ready && (pentry->token_count <= 0)) {
533 active_readers_.Remove(pentry);
534 }
535 }
536 }
537
538 virtual void ReturnTokens(Dart_Port port, int count) {
539 SimpleHashMap::Entry* entry = tokens_map_.Lookup(
540 GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
541 ASSERT(entry != nullptr);
542
543 PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
544 bool was_ready = pentry->IsReady();
545 if (!disable_tokens_) {
546 pentry->token_count += count;
547 }
548 ASSERT(pentry->token_count <= kTokenCount);
549 bool is_ready = pentry->IsReady();
550 if (!was_ready && is_ready) {
551 active_readers_.Add(pentry);
552 }
553 }
554
555 virtual intptr_t Mask() {
556 if (active_readers_.HasHead()) {
557 return 1 << kInEvent;
558 }
559 return 0;
560 }
561
562 virtual void Close() { DI::Close(); }
563
564 private:
565 static void DeletePortEntry(void* data) {
566 PortEntry* entry = reinterpret_cast<PortEntry*>(data);
567 delete entry;
568 }
569
570 // The [Dart_Port]s which are not paused (i.e. are interested in read events,
571 // i.e. `mask == (1 << kInEvent)`) and we have enough tokens to communicate
572 // with them.
573 CircularLinkedList<PortEntry*> active_readers_;
574
575 // A convenience mapping:
576 // Dart_Port -> struct PortEntry { dart_port, mask, token_count }
577 SimpleHashMap tokens_map_;
578
579 bool disable_tokens_;
580
581 DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultipleMixin);
582};
583
584} // namespace bin
585} // namespace dart
586
587// The event handler delegation class is OS specific.
588#if defined(DART_HOST_OS_FUCHSIA)
590#elif defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
592#elif defined(DART_HOST_OS_MACOS)
594#elif defined(DART_HOST_OS_WINDOWS)
595#include "bin/eventhandler_win.h"
596#else
597#error Unknown target os.
598#endif
599
600namespace dart {
601namespace bin {
602
604 public:
606 void SendData(intptr_t id, Dart_Port dart_port, int64_t data) {
607 delegate_.SendData(id, dart_port, data);
608 }
609
610 /**
611 * Signal to main thread that event handler is done.
612 */
613 void NotifyShutdownDone();
614
615 /**
616 * Start the event-handler.
617 */
618 static void Start();
619
620 /**
621 * Stop the event-handler. It's expected that there will be no further calls
622 * to SendData after a call to Stop.
623 */
624 static void Stop();
625
627
628 static void SendFromNative(intptr_t id, Dart_Port port, int64_t data);
629
630 private:
633
635};
636
637} // namespace bin
638} // namespace dart
639
640#endif // RUNTIME_BIN_EVENTHANDLER_H_
int count
Definition: FontMgrTest.cpp:50
static float next(float f)
static float prev(float f)
bool RemoveByValue(const V &value)
const Entry & Minimum() const
bool InsertOrChangePriority(const P &priority, const V &value)
bool IsEmpty() const
void Clear(ClearFun clear=nullptr)
Definition: hashmap.cc:111
Entry * Start() const
Definition: hashmap.cc:123
Entry * Lookup(void *key, uint32_t hash, bool insert)
Definition: hashmap.cc:20
void Remove(void *key, uint32_t hash)
Definition: hashmap.cc:49
Entry * Next(Entry *p) const
Definition: hashmap.cc:127
void RemoveAll(ClearFun clear=nullptr)
Definition: eventhandler.h:185
void(* ClearFun)(void *value)
Definition: eventhandler.h:117
void RemoveHead(ClearFun clear=nullptr)
Definition: eventhandler.h:138
static bool PostInt32(Dart_Port port_id, int32_t value)
Definition: dartutils.cc:672
virtual void RemoveAllPorts()=0
virtual void RemovePort(Dart_Port port)=0
virtual void ReturnTokens(Dart_Port port, int count)=0
virtual void SetPortAndMask(Dart_Port port, intptr_t mask)=0
virtual void NotifyAllDartPorts(uintptr_t events)=0
virtual Dart_Port NextNotifyDartPort(intptr_t events_ready)=0
virtual intptr_t Mask()=0
virtual bool IsListeningSocket() const =0
virtual void SetPortAndMask(Dart_Port port, intptr_t mask)
Definition: eventhandler.h:401
DescriptorInfoMultipleMixin(intptr_t fd, bool disable_tokens)
Definition: eventhandler.h:392
virtual void RemovePort(Dart_Port port)
Definition: eventhandler.h:455
virtual void NotifyAllDartPorts(uintptr_t events)
Definition: eventhandler.h:515
virtual Dart_Port NextNotifyDartPort(intptr_t events_ready)
Definition: eventhandler.h:491
virtual void ReturnTokens(Dart_Port port, int count)
Definition: eventhandler.h:538
virtual bool IsListeningSocket() const
Definition: eventhandler.h:399
virtual bool IsListeningSocket() const
Definition: eventhandler.h:281
virtual Dart_Port NextNotifyDartPort(intptr_t events_ready)
Definition: eventhandler.h:302
virtual void ReturnTokens(Dart_Port port, int count)
Definition: eventhandler.h:325
virtual void SetPortAndMask(Dart_Port port, intptr_t mask)
Definition: eventhandler.h:283
DescriptorInfoSingleMixin(intptr_t fd, bool disable_tokens)
Definition: eventhandler.h:272
virtual void NotifyAllDartPorts(uintptr_t events)
Definition: eventhandler.h:311
virtual void RemovePort(Dart_Port port)
Definition: eventhandler.h:289
void SendData(intptr_t id, Dart_Port dart_port, int64_t data)
static EventHandlerImplementation * delegate()
Definition: eventhandler.cc:64
static void SendFromNative(intptr_t id, Dart_Port port, int64_t data)
Definition: eventhandler.cc:71
void SendData(intptr_t id, Dart_Port dart_port, int64_t data)
Definition: eventhandler.h:606
Dart_Port CurrentPort() const
Definition: eventhandler.h:79
void UpdateTimeout(Dart_Port port, int64_t timeout)
Definition: eventhandler.h:86
int64_t CurrentTimeout() const
Definition: eventhandler.h:74
bool HasTimeout() const
Definition: eventhandler.h:72
int64_t Dart_Port
Definition: dart_api.h:1525
MockDelegate delegate_
#define ASSERT(E)
#define IS_IO_EVENT(data)
Definition: eventhandler.h:53
#define IS_EVENT(data, event_bit)
Definition: eventhandler.h:51
uint8_t value
static constexpr intptr_t kTimerId
Definition: eventhandler.h:109
@ kShutdownWriteCommand
Definition: eventhandler.h:30
@ kListeningSocket
Definition: eventhandler.h:33
@ kReturnTokenCommand
Definition: eventhandler.h:31
@ kShutdownReadCommand
Definition: eventhandler.h:29
@ kDestroyedEvent
Definition: eventhandler.h:27
@ kSetEventMaskCommand
Definition: eventhandler.h:32
static constexpr intptr_t kShutdownId
Definition: eventhandler.h:110
static constexpr intptr_t kInfinityTimeout
Definition: eventhandler.h:108
static constexpr intptr_t kInterruptMessageSize
Definition: eventhandler.h:107
Definition: dart_vm.cc:33
static int8_t data[kExtLength]
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service port
Definition: switches.h:87
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
def timeout(deadline, cmd)
string root
Definition: scale_cpu.py:20
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: globals.h:581
#define T
Definition: precompiler.cc:65