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

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

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