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