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

Side by Side Diff: dart/runtime/bin/eventhandler_macos.cc

Issue 879353003: Introduce optional 'bool shared' parameter to ServerSocket.bind() ... (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressed comments Created 5 years, 10 months 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 | « dart/runtime/bin/eventhandler_macos.h ('k') | dart/runtime/bin/eventhandler_win.h » ('j') | 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) 2012, the Dart project authors. Please see the AUTHORS 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 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 #include "platform/globals.h" 5 #include "platform/globals.h"
6 #if defined(TARGET_OS_MACOS) 6 #if defined(TARGET_OS_MACOS)
7 7
8 #include "bin/eventhandler.h" 8 #include "bin/eventhandler.h"
9 #include "bin/eventhandler_macos.h"
9 10
10 #include <errno.h> // NOLINT 11 #include <errno.h> // NOLINT
11 #include <pthread.h> // NOLINT 12 #include <pthread.h> // NOLINT
12 #include <stdio.h> // NOLINT 13 #include <stdio.h> // NOLINT
13 #include <string.h> // NOLINT 14 #include <string.h> // NOLINT
14 #include <sys/event.h> // NOLINT 15 #include <sys/event.h> // NOLINT
15 #include <unistd.h> // NOLINT 16 #include <unistd.h> // NOLINT
16 #include <fcntl.h> // NOLINT 17 #include <fcntl.h> // NOLINT
17 18
18 #include "bin/dartutils.h" 19 #include "bin/dartutils.h"
19 #include "bin/fdutils.h" 20 #include "bin/fdutils.h"
21 #include "bin/lockers.h"
20 #include "bin/log.h" 22 #include "bin/log.h"
23 #include "bin/socket.h"
21 #include "bin/thread.h" 24 #include "bin/thread.h"
22 #include "bin/utils.h" 25 #include "bin/utils.h"
23 #include "platform/hashmap.h" 26 #include "platform/hashmap.h"
24 #include "platform/utils.h" 27 #include "platform/utils.h"
25 28
26 29
27 namespace dart { 30 namespace dart {
28 namespace bin { 31 namespace bin {
29 32
30 static const int kInterruptMessageSize = sizeof(InterruptMessage);
31 static const int kInfinityTimeout = -1;
32 static const int kTimerId = -1;
33 static const int kShutdownId = -2;
34 33
35 34 bool DescriptorInfo::HasReadEvent() {
36 bool SocketData::HasReadEvent() { 35 return (Mask() & (1 << kInEvent)) != 0;
37 return (mask_ & (1 << kInEvent)) != 0;
38 } 36 }
39 37
40 38
41 bool SocketData::HasWriteEvent() { 39 bool DescriptorInfo::HasWriteEvent() {
42 return (mask_ & (1 << kOutEvent)) != 0; 40 return (Mask() & (1 << kOutEvent)) != 0;
43 } 41 }
44 42
45 43
46 // Unregister the file descriptor for a SocketData structure with kqueue. 44 // Unregister the file descriptor for a SocketData structure with kqueue.
47 static void RemoveFromKqueue(intptr_t kqueue_fd_, SocketData* sd) { 45 static void RemoveFromKqueue(intptr_t kqueue_fd_, DescriptorInfo* di) {
48 if (!sd->tracked_by_kqueue()) return; 46 if (!di->tracked_by_kqueue()) return;
49 static const intptr_t kMaxChanges = 2; 47 static const intptr_t kMaxChanges = 2;
50 struct kevent events[kMaxChanges]; 48 struct kevent events[kMaxChanges];
51 EV_SET(events, sd->fd(), EVFILT_READ, EV_DELETE, 0, 0, NULL); 49 EV_SET(events, di->fd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
52 VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL)); 50 VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL));
53 EV_SET(events, sd->fd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 51 EV_SET(events, di->fd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
54 VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL)); 52 VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL));
55 sd->set_tracked_by_kqueue(false); 53 di->set_tracked_by_kqueue(false);
56 } 54 }
57 55
58 56
59 // Update the kqueue registration for SocketData structure to reflect 57 // Update the kqueue registration for SocketData structure to reflect
60 // the events currently of interest. 58 // the events currently of interest.
61 static void AddToKqueue(intptr_t kqueue_fd_, SocketData* sd) { 59 static void AddToKqueue(intptr_t kqueue_fd_, DescriptorInfo* di) {
62 ASSERT(!sd->tracked_by_kqueue()); 60 ASSERT(!di->tracked_by_kqueue());
63 static const intptr_t kMaxChanges = 2; 61 static const intptr_t kMaxChanges = 2;
64 intptr_t changes = 0; 62 intptr_t changes = 0;
65 struct kevent events[kMaxChanges]; 63 struct kevent events[kMaxChanges];
66 int flags = EV_ADD; 64 int flags = EV_ADD;
67 if (!sd->IsListeningSocket()) { 65 if (!di->IsListeningSocket()) {
68 flags |= EV_CLEAR; 66 flags |= EV_CLEAR;
69 } 67 }
70 // Register or unregister READ filter if needed. 68 // Register or unregister READ filter if needed.
71 if (sd->HasReadEvent()) { 69 if (di->HasReadEvent()) {
72 EV_SET(events + changes, 70 EV_SET(events + changes,
73 sd->fd(), 71 di->fd(),
74 EVFILT_READ, 72 EVFILT_READ,
75 flags, 73 flags,
76 0, 74 0,
77 0, 75 0,
78 sd); 76 di);
79 ++changes; 77 ++changes;
80 } 78 }
81 // Register or unregister WRITE filter if needed. 79 // Register or unregister WRITE filter if needed.
82 if (sd->HasWriteEvent()) { 80 if (di->HasWriteEvent()) {
83 EV_SET(events + changes, 81 EV_SET(events + changes,
84 sd->fd(), 82 di->fd(),
85 EVFILT_WRITE, 83 EVFILT_WRITE,
86 flags, 84 flags,
87 0, 85 0,
88 0, 86 0,
89 sd); 87 di);
90 ++changes; 88 ++changes;
91 } 89 }
92 ASSERT(changes > 0); 90 ASSERT(changes > 0);
93 ASSERT(changes <= kMaxChanges); 91 ASSERT(changes <= kMaxChanges);
94 int status = 92 int status =
95 NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, changes, NULL, 0, NULL)); 93 NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, changes, NULL, 0, NULL));
96 if (status == -1) { 94 if (status == -1) {
95 // TODO(kustermann): Verify that the dart end is handling this correctly &
96 // adapt this code to work for multiple listening sockets.
97
97 // kQueue does not accept the file descriptor. It could be due to 98 // kQueue does not accept the file descriptor. It could be due to
98 // already closed file descriptor, or unuspported devices, such 99 // already closed file descriptor, or unuspported devices, such
99 // as /dev/null. In such case, mark the file descriptor as closed, 100 // as /dev/null. In such case, mark the file descriptor as closed,
100 // so dart will handle it accordingly. 101 // so dart will handle it accordingly.
101 DartUtils::PostInt32(sd->port(), 1 << kCloseEvent); 102 DartUtils::PostInt32(di->NextPort(), 1 << kCloseEvent);
102 } else { 103 } else {
103 sd->set_tracked_by_kqueue(true); 104 di->set_tracked_by_kqueue(true);
104 } 105 }
105 } 106 }
106 107
107 108
108 EventHandlerImplementation::EventHandlerImplementation() 109 EventHandlerImplementation::EventHandlerImplementation()
109 : socket_map_(&HashMap::SamePointerValue, 16) { 110 : socket_map_(&HashMap::SamePointerValue, 16) {
110 intptr_t result; 111 intptr_t result;
111 result = NO_RETRY_EXPECTED(pipe(interrupt_fds_)); 112 result = NO_RETRY_EXPECTED(pipe(interrupt_fds_));
112 if (result != 0) { 113 if (result != 0) {
113 FATAL("Pipe creation failed"); 114 FATAL("Pipe creation failed");
(...skipping 21 matching lines...) Expand all
135 } 136 }
136 137
137 138
138 EventHandlerImplementation::~EventHandlerImplementation() { 139 EventHandlerImplementation::~EventHandlerImplementation() {
139 VOID_TEMP_FAILURE_RETRY(close(kqueue_fd_)); 140 VOID_TEMP_FAILURE_RETRY(close(kqueue_fd_));
140 VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0])); 141 VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[0]));
141 VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[1])); 142 VOID_TEMP_FAILURE_RETRY(close(interrupt_fds_[1]));
142 } 143 }
143 144
144 145
145 SocketData* EventHandlerImplementation::GetSocketData(intptr_t fd) { 146 DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
147 intptr_t fd, bool is_listening) {
146 ASSERT(fd >= 0); 148 ASSERT(fd >= 0);
147 HashMap::Entry* entry = socket_map_.Lookup( 149 HashMap::Entry* entry = socket_map_.Lookup(
148 GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true); 150 GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
149 ASSERT(entry != NULL); 151 ASSERT(entry != NULL);
150 SocketData* sd = reinterpret_cast<SocketData*>(entry->value); 152 DescriptorInfo* di =
151 if (sd == NULL) { 153 reinterpret_cast<DescriptorInfo*>(entry->value);
154 if (di == NULL) {
152 // If there is no data in the hash map for this file descriptor a 155 // If there is no data in the hash map for this file descriptor a
153 // new SocketData for the file descriptor is inserted. 156 // new DescriptorInfo for the file descriptor is inserted.
154 sd = new SocketData(fd); 157 if (is_listening) {
155 entry->value = sd; 158 di = new DescriptorInfoMultiple(fd);
159 } else {
160 di = new DescriptorInfoSingle(fd);
161 }
162 entry->value = di;
156 } 163 }
157 ASSERT(fd == sd->fd()); 164 ASSERT(fd == di->fd());
158 return sd; 165 return di;
159 } 166 }
160 167
161 168
162 void EventHandlerImplementation::WakeupHandler(intptr_t id, 169 void EventHandlerImplementation::WakeupHandler(intptr_t id,
163 Dart_Port dart_port, 170 Dart_Port dart_port,
164 int64_t data) { 171 int64_t data) {
165 InterruptMessage msg; 172 InterruptMessage msg;
166 msg.id = id; 173 msg.id = id;
167 msg.dart_port = dart_port; 174 msg.dart_port = dart_port;
168 msg.data = data; 175 msg.data = data;
(...skipping 15 matching lines...) Expand all
184 const intptr_t MAX_MESSAGES = kInterruptMessageSize; 191 const intptr_t MAX_MESSAGES = kInterruptMessageSize;
185 InterruptMessage msg[MAX_MESSAGES]; 192 InterruptMessage msg[MAX_MESSAGES];
186 ssize_t bytes = TEMP_FAILURE_RETRY( 193 ssize_t bytes = TEMP_FAILURE_RETRY(
187 read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize)); 194 read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize));
188 for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) { 195 for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) {
189 if (msg[i].id == kTimerId) { 196 if (msg[i].id == kTimerId) {
190 timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data); 197 timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data);
191 } else if (msg[i].id == kShutdownId) { 198 } else if (msg[i].id == kShutdownId) {
192 shutdown_ = true; 199 shutdown_ = true;
193 } else { 200 } else {
194 SocketData* sd = GetSocketData(msg[i].id); 201 DescriptorInfo* di = GetDescriptorInfo(
202 msg[i].id, (msg[i].data & (1 << kListeningSocket)) != 0);
195 if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) { 203 if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
204 ASSERT(!di->IsListeningSocket());
196 // Close the socket for reading. 205 // Close the socket for reading.
197 shutdown(sd->fd(), SHUT_RD); 206 VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
198 } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) { 207 } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
208 ASSERT(!di->IsListeningSocket());
199 // Close the socket for writing. 209 // Close the socket for writing.
200 shutdown(sd->fd(), SHUT_WR); 210 VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
201 } else if (IS_COMMAND(msg[i].data, kCloseCommand)) { 211 } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
202 // Close the socket and free system resources. 212 // Close the socket and free system resources and move on to next
203 RemoveFromKqueue(kqueue_fd_, sd); 213 // message.
204 intptr_t fd = sd->fd(); 214 bool no_more_listeners = di->RemovePort(msg[i].dart_port);
205 VOID_TEMP_FAILURE_RETRY(close(fd)); 215 if (no_more_listeners) {
206 socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd)); 216 RemoveFromKqueue(kqueue_fd_, di);
207 delete sd; 217 }
218
219 intptr_t fd = di->fd();
220 if (di->IsListeningSocket()) {
221 // We only close the socket file descriptor from the operating
222 // system if there are no other dart socket objects which
223 // are listening on the same (address, port) combination.
224 {
225 MutexLocker ml(globalTcpListeningSocketRegistry.mutex());
226 if (globalTcpListeningSocketRegistry.CloseSafe(fd)) {
227 socket_map_.Remove(
228 GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
229 di->Close();
230 delete di;
231 }
232 }
233 } else {
234 ASSERT(no_more_listeners);
235 socket_map_.Remove(
236 GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
237 di->Close();
238 delete di;
239 }
208 DartUtils::PostInt32(msg[i].dart_port, 1 << kDestroyedEvent); 240 DartUtils::PostInt32(msg[i].dart_port, 1 << kDestroyedEvent);
209 } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) { 241 } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
210 int count = TOKEN_COUNT(msg[i].data); 242 int count = TOKEN_COUNT(msg[i].data);
211 for (int i = 0; i < count; i++) { 243 if (di->ReturnTokens(msg[i].dart_port, count)) {
212 if (sd->ReturnToken()) { 244 AddToKqueue(kqueue_fd_, di);
213 AddToKqueue(kqueue_fd_, sd);
214 }
215 } 245 }
216 } else { 246 } else {
217 ASSERT_NO_COMMAND(msg[i].data); 247 ASSERT_NO_COMMAND(msg[i].data);
218 // Setup events to wait for.
219 ASSERT((msg[i].data > 0) && (msg[i].data < kIntptrMax)); 248 ASSERT((msg[i].data > 0) && (msg[i].data < kIntptrMax));
220 ASSERT(sd->port() == 0); 249 bool had_listeners = di->HasNextPort();
221 sd->SetPortAndMask(msg[i].dart_port, 250 di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
222 static_cast<intptr_t>(msg[i].data)); 251 bool has_listeners = di->HasNextPort();
223 AddToKqueue(kqueue_fd_, sd); 252
253 // Add/Remove from epoll set depending on previous and current state.
254 if (!had_listeners && has_listeners) {
255 AddToKqueue(kqueue_fd_, di);
256 } else if (had_listeners && !has_listeners) {
257 RemoveFromKqueue(kqueue_fd_, di);
258 }
224 } 259 }
225 } 260 }
226 } 261 }
227 } 262 }
228 263
229 #ifdef DEBUG_KQUEUE 264 #ifdef DEBUG_KQUEUE
230 static void PrintEventMask(intptr_t fd, struct kevent* event) { 265 static void PrintEventMask(intptr_t fd, struct kevent* event) {
231 Log::Print("%d ", static_cast<int>(fd)); 266 Log::Print("%d ", static_cast<int>(fd));
232 Log::Print("filter=0x%x:", event->filter); 267 Log::Print("filter=0x%x:", event->filter);
233 if (event->filter == EVFILT_READ) Log::Print("EVFILT_READ "); 268 if (event->filter == EVFILT_READ) Log::Print("EVFILT_READ ");
234 if (event->filter == EVFILT_WRITE) Log::Print("EVFILT_WRITE "); 269 if (event->filter == EVFILT_WRITE) Log::Print("EVFILT_WRITE ");
235 Log::Print("flags: %x: ", event->flags); 270 Log::Print("flags: %x: ", event->flags);
236 if ((event->flags & EV_EOF) != 0) Log::Print("EV_EOF "); 271 if ((event->flags & EV_EOF) != 0) Log::Print("EV_EOF ");
237 if ((event->flags & EV_ERROR) != 0) Log::Print("EV_ERROR "); 272 if ((event->flags & EV_ERROR) != 0) Log::Print("EV_ERROR ");
238 if ((event->flags & EV_CLEAR) != 0) Log::Print("EV_CLEAR "); 273 if ((event->flags & EV_CLEAR) != 0) Log::Print("EV_CLEAR ");
239 if ((event->flags & EV_ADD) != 0) Log::Print("EV_ADD "); 274 if ((event->flags & EV_ADD) != 0) Log::Print("EV_ADD ");
240 if ((event->flags & EV_DELETE) != 0) Log::Print("EV_DELETE "); 275 if ((event->flags & EV_DELETE) != 0) Log::Print("EV_DELETE ");
241 Log::Print("- fflags: %d ", event->fflags); 276 Log::Print("- fflags: %d ", event->fflags);
242 Log::Print("- data: %ld ", event->data); 277 Log::Print("- data: %ld ", event->data);
243 Log::Print("(available %d) ", 278 Log::Print("(available %d) ",
244 static_cast<int>(FDUtils::AvailableBytes(fd))); 279 static_cast<int>(FDUtils::AvailableBytes(fd)));
245 Log::Print("\n"); 280 Log::Print("\n");
246 } 281 }
247 #endif 282 #endif
248 283
249 284
250 intptr_t EventHandlerImplementation::GetEvents(struct kevent* event, 285 intptr_t EventHandlerImplementation::GetEvents(struct kevent* event,
251 SocketData* sd) { 286 DescriptorInfo* di) {
252 #ifdef DEBUG_KQUEUE 287 #ifdef DEBUG_KQUEUE
253 PrintEventMask(sd->fd(), event); 288 PrintEventMask(di->fd(), event);
254 #endif 289 #endif
255 intptr_t event_mask = 0; 290 intptr_t event_mask = 0;
256 if (sd->IsListeningSocket()) { 291 if (di->IsListeningSocket()) {
257 // On a listening socket the READ event means that there are 292 // On a listening socket the READ event means that there are
258 // connections ready to be accepted. 293 // connections ready to be accepted.
259 if (event->filter == EVFILT_READ) { 294 if (event->filter == EVFILT_READ) {
260 if ((event->flags & EV_EOF) != 0) { 295 if ((event->flags & EV_EOF) != 0) {
261 if (event->fflags != 0) { 296 if (event->fflags != 0) {
262 event_mask |= (1 << kErrorEvent); 297 event_mask |= (1 << kErrorEvent);
263 } else { 298 } else {
264 event_mask |= (1 << kCloseEvent); 299 event_mask |= (1 << kCloseEvent);
265 } 300 }
266 } 301 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 // If flag EV_ERROR is set it indicates an error in kevent processing. 337 // If flag EV_ERROR is set it indicates an error in kevent processing.
303 if ((events[i].flags & EV_ERROR) != 0) { 338 if ((events[i].flags & EV_ERROR) != 0) {
304 const int kBufferSize = 1024; 339 const int kBufferSize = 1024;
305 char error_message[kBufferSize]; 340 char error_message[kBufferSize];
306 strerror_r(events[i].data, error_message, kBufferSize); 341 strerror_r(events[i].data, error_message, kBufferSize);
307 FATAL1("kevent failed %s\n", error_message); 342 FATAL1("kevent failed %s\n", error_message);
308 } 343 }
309 if (events[i].udata == NULL) { 344 if (events[i].udata == NULL) {
310 interrupt_seen = true; 345 interrupt_seen = true;
311 } else { 346 } else {
312 SocketData* sd = reinterpret_cast<SocketData*>(events[i].udata); 347 DescriptorInfo* di =
313 intptr_t event_mask = GetEvents(events + i, sd); 348 reinterpret_cast<DescriptorInfo*>(events[i].udata);
349 intptr_t event_mask = GetEvents(events + i, di);
314 if (event_mask != 0) { 350 if (event_mask != 0) {
315 if (sd->TakeToken()) { 351 Dart_Port port = di->NextPort();
316 // Took last token, remove from epoll. 352 ASSERT(port != 0);
317 RemoveFromKqueue(kqueue_fd_, sd); 353 if (di->TakeToken()) {
354 // Took last token, remove from kqueue.
355 RemoveFromKqueue(kqueue_fd_, di);
318 } 356 }
319 Dart_Port port = sd->port();
320 ASSERT(port != 0);
321 DartUtils::PostInt32(port, event_mask); 357 DartUtils::PostInt32(port, event_mask);
322 } 358 }
323 } 359 }
324 } 360 }
325 if (interrupt_seen) { 361 if (interrupt_seen) {
326 // Handle after socket events, so we avoid closing a socket before we handle 362 // Handle after socket events, so we avoid closing a socket before we handle
327 // the current events. 363 // the current events.
328 HandleInterruptFd(); 364 HandleInterruptFd();
329 } 365 }
330 } 366 }
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 457
422 uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) { 458 uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) {
423 // The hashmap does not support keys with value 0. 459 // The hashmap does not support keys with value 0.
424 return dart::Utils::WordHash(fd + 1); 460 return dart::Utils::WordHash(fd + 1);
425 } 461 }
426 462
427 } // namespace bin 463 } // namespace bin
428 } // namespace dart 464 } // namespace dart
429 465
430 #endif // defined(TARGET_OS_MACOS) 466 #endif // defined(TARGET_OS_MACOS)
OLDNEW
« no previous file with comments | « dart/runtime/bin/eventhandler_macos.h ('k') | dart/runtime/bin/eventhandler_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698