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

Side by Side Diff: runtime/vm/isolate.cc

Issue 1965823002: Initial isolate reload support (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 7 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 "vm/isolate.h" 5 #include "vm/isolate.h"
6 6
7 #include "include/dart_api.h" 7 #include "include/dart_api.h"
8 #include "include/dart_native_api.h" 8 #include "include/dart_native_api.h"
9 #include "platform/assert.h" 9 #include "platform/assert.h"
10 #include "platform/text_buffer.h" 10 #include "platform/text_buffer.h"
11 #include "vm/class_finalizer.h" 11 #include "vm/class_finalizer.h"
12 #include "vm/code_observers.h" 12 #include "vm/code_observers.h"
13 #include "vm/compiler.h" 13 #include "vm/compiler.h"
14 #include "vm/compiler_stats.h" 14 #include "vm/compiler_stats.h"
15 #include "vm/dart_api_message.h" 15 #include "vm/dart_api_message.h"
16 #include "vm/dart_api_state.h" 16 #include "vm/dart_api_state.h"
17 #include "vm/dart_entry.h" 17 #include "vm/dart_entry.h"
18 #include "vm/debugger.h" 18 #include "vm/debugger.h"
19 #include "vm/deopt_instructions.h" 19 #include "vm/deopt_instructions.h"
20 #include "vm/flags.h" 20 #include "vm/flags.h"
21 #include "vm/heap.h" 21 #include "vm/heap.h"
22 #include "vm/isolate_reload.h"
22 #include "vm/lockers.h" 23 #include "vm/lockers.h"
23 #include "vm/log.h" 24 #include "vm/log.h"
24 #include "vm/message_handler.h" 25 #include "vm/message_handler.h"
25 #include "vm/object_id_ring.h" 26 #include "vm/object_id_ring.h"
26 #include "vm/object_store.h" 27 #include "vm/object_store.h"
27 #include "vm/object.h" 28 #include "vm/object.h"
28 #include "vm/os_thread.h" 29 #include "vm/os_thread.h"
29 #include "vm/port.h" 30 #include "vm/port.h"
30 #include "vm/profiler.h" 31 #include "vm/profiler.h"
31 #include "vm/reusable_handles.h" 32 #include "vm/reusable_handles.h"
(...skipping 13 matching lines...) Expand all
45 #include "vm/timeline_analysis.h" 46 #include "vm/timeline_analysis.h"
46 #include "vm/timer.h" 47 #include "vm/timer.h"
47 #include "vm/visitor.h" 48 #include "vm/visitor.h"
48 49
49 50
50 namespace dart { 51 namespace dart {
51 52
52 DECLARE_FLAG(bool, print_metrics); 53 DECLARE_FLAG(bool, print_metrics);
53 DECLARE_FLAG(bool, timing); 54 DECLARE_FLAG(bool, timing);
54 DECLARE_FLAG(bool, trace_service); 55 DECLARE_FLAG(bool, trace_service);
56 DECLARE_FLAG(bool, trace_reload);
55 DECLARE_FLAG(bool, warn_on_pause_with_no_debugger); 57 DECLARE_FLAG(bool, warn_on_pause_with_no_debugger);
56 58
57 NOT_IN_PRODUCT( 59 NOT_IN_PRODUCT(
58 static void CheckedModeHandler(bool value) { 60 static void CheckedModeHandler(bool value) {
59 FLAG_enable_asserts = value; 61 FLAG_enable_asserts = value;
60 FLAG_enable_type_checks = value; 62 FLAG_enable_type_checks = value;
61 } 63 }
62 64
63 // --enable-checked-mode and --checked both enable checked mode which is 65 // --enable-checked-mode and --checked both enable checked mode which is
64 // equivalent to setting --enable-asserts and --enable-type-checks. 66 // equivalent to setting --enable-asserts and --enable-type-checks.
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 thread->DeferOOBMessageInterrupts(); 129 thread->DeferOOBMessageInterrupts();
128 } 130 }
129 131
130 132
131 NoOOBMessageScope::~NoOOBMessageScope() { 133 NoOOBMessageScope::~NoOOBMessageScope() {
132 thread()->RestoreOOBMessageInterrupts(); 134 thread()->RestoreOOBMessageInterrupts();
133 } 135 }
134 136
135 137
136 138
139 NoReloadScope::NoReloadScope(Isolate* isolate, Thread* thread)
140 : StackResource(thread),
141 isolate_(isolate) {
142 ASSERT(isolate_ != NULL);
143 isolate_->no_reload_scope_depth_++;
144 ASSERT(isolate_->no_reload_scope_depth_ >= 0);
145 }
146
147
148 NoReloadScope::~NoReloadScope() {
149 isolate_->no_reload_scope_depth_--;
150 ASSERT(isolate_->no_reload_scope_depth_ >= 0);
151 }
152
153
137 void Isolate::RegisterClass(const Class& cls) { 154 void Isolate::RegisterClass(const Class& cls) {
155 NOT_IN_PRODUCT(
156 if (IsReloading()) {
157 reload_context()->RegisterClass(cls);
158 return;
159 }
160 )
138 class_table()->Register(cls); 161 class_table()->Register(cls);
139 } 162 }
140 163
141 164
142 void Isolate::RegisterClassAt(intptr_t index, const Class& cls) { 165 void Isolate::RegisterClassAt(intptr_t index, const Class& cls) {
143 class_table()->RegisterAt(index, cls); 166 class_table()->RegisterAt(index, cls);
144 } 167 }
145 168
146 169
147 void Isolate::ValidateClassTable() { 170 void Isolate::ValidateClassTable() {
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after
617 return MessageHandler::kShutdown; 640 return MessageHandler::kShutdown;
618 } 641 }
619 } 642 }
620 } 643 }
621 return MessageHandler::kError; 644 return MessageHandler::kError;
622 } 645 }
623 646
624 647
625 MessageHandler::MessageStatus IsolateMessageHandler::ProcessUnhandledException( 648 MessageHandler::MessageStatus IsolateMessageHandler::ProcessUnhandledException(
626 const Error& result) { 649 const Error& result) {
650 NOT_IN_PRODUCT(
651 if (I->IsReloading()) {
652 I->ReportReloadError(result);
653 return kOK;
654 }
655 )
627 // Generate the error and stacktrace strings for the error message. 656 // Generate the error and stacktrace strings for the error message.
628 String& exc_str = String::Handle(T->zone()); 657 String& exc_str = String::Handle(T->zone());
629 String& stacktrace_str = String::Handle(T->zone()); 658 String& stacktrace_str = String::Handle(T->zone());
630 if (result.IsUnhandledException()) { 659 if (result.IsUnhandledException()) {
631 Zone* zone = T->zone(); 660 Zone* zone = T->zone();
632 const UnhandledException& uhe = UnhandledException::Cast(result); 661 const UnhandledException& uhe = UnhandledException::Cast(result);
633 const Instance& exception = Instance::Handle(zone, uhe.exception()); 662 const Instance& exception = Instance::Handle(zone, uhe.exception());
634 Object& tmp = Object::Handle(zone); 663 Object& tmp = Object::Handle(zone);
635 tmp = DartLibraryCalls::ToString(exception); 664 tmp = DartLibraryCalls::ToString(exception);
636 if (!tmp.IsString()) { 665 if (!tmp.IsString()) {
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 is_service_isolate_(false), 807 is_service_isolate_(false),
779 stacktrace_(NULL), 808 stacktrace_(NULL),
780 stack_frame_index_(-1), 809 stack_frame_index_(-1),
781 last_allocationprofile_accumulator_reset_timestamp_(0), 810 last_allocationprofile_accumulator_reset_timestamp_(0),
782 last_allocationprofile_gc_timestamp_(0), 811 last_allocationprofile_gc_timestamp_(0),
783 object_id_ring_(NULL), 812 object_id_ring_(NULL),
784 tag_table_(GrowableObjectArray::null()), 813 tag_table_(GrowableObjectArray::null()),
785 deoptimized_code_array_(GrowableObjectArray::null()), 814 deoptimized_code_array_(GrowableObjectArray::null()),
786 sticky_error_(Error::null()), 815 sticky_error_(Error::null()),
787 background_compiler_(NULL), 816 background_compiler_(NULL),
817 background_compiler_disabled_depth_(0),
788 pending_service_extension_calls_(GrowableObjectArray::null()), 818 pending_service_extension_calls_(GrowableObjectArray::null()),
789 registered_service_extension_handlers_(GrowableObjectArray::null()), 819 registered_service_extension_handlers_(GrowableObjectArray::null()),
790 metrics_list_head_(NULL), 820 metrics_list_head_(NULL),
791 compilation_allowed_(true), 821 compilation_allowed_(true),
792 all_classes_finalized_(false), 822 all_classes_finalized_(false),
793 next_(NULL), 823 next_(NULL),
794 pause_loop_monitor_(NULL), 824 pause_loop_monitor_(NULL),
795 field_invalidation_gen_(kInvalidGen), 825 field_invalidation_gen_(kInvalidGen),
796 loading_invalidation_gen_(kInvalidGen), 826 loading_invalidation_gen_(kInvalidGen),
797 top_level_parsing_count_(0), 827 top_level_parsing_count_(0),
798 field_list_mutex_(new Mutex()), 828 field_list_mutex_(new Mutex()),
799 boxed_field_list_(GrowableObjectArray::null()), 829 boxed_field_list_(GrowableObjectArray::null()),
800 disabling_field_list_(GrowableObjectArray::null()), 830 disabling_field_list_(GrowableObjectArray::null()),
801 spawn_count_monitor_(new Monitor()), 831 spawn_count_monitor_(new Monitor()),
802 spawn_count_(0) { 832 spawn_count_(0),
833 has_attempted_reload_(false),
834 no_reload_scope_depth_(0),
835 reload_context_(NULL) {
803 NOT_IN_PRODUCT(FlagsCopyFrom(api_flags)); 836 NOT_IN_PRODUCT(FlagsCopyFrom(api_flags));
804 // TODO(asiva): A Thread is not available here, need to figure out 837 // TODO(asiva): A Thread is not available here, need to figure out
805 // how the vm_tag (kEmbedderTagId) can be set, these tags need to 838 // how the vm_tag (kEmbedderTagId) can be set, these tags need to
806 // move to the OSThread structure. 839 // move to the OSThread structure.
807 set_user_tag(UserTags::kDefaultUserTag); 840 set_user_tag(UserTags::kDefaultUserTag);
808 } 841 }
809 842
810 #undef REUSABLE_HANDLE_SCOPE_INIT 843 #undef REUSABLE_HANDLE_SCOPE_INIT
811 #undef REUSABLE_HANDLE_INITIALIZERS 844 #undef REUSABLE_HANDLE_INITIALIZERS
812 845
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
1009 // as 'load in progres'. Set the status to 'loaded'. 1042 // as 'load in progres'. Set the status to 'loaded'.
1010 if (lib.LoadInProgress()) { 1043 if (lib.LoadInProgress()) {
1011 lib.SetLoaded(); 1044 lib.SetLoaded();
1012 } 1045 }
1013 lib.InitExportedNamesCache(); 1046 lib.InitExportedNamesCache();
1014 } 1047 }
1015 TokenStream::CloseSharedTokenList(this); 1048 TokenStream::CloseSharedTokenList(this);
1016 } 1049 }
1017 1050
1018 1051
1052 bool Isolate::CanReload() const {
1053 #ifndef PRODUCT
1054 return (!ServiceIsolate::IsServiceIsolateDescendant(this) &&
1055 is_runnable() && !IsReloading() && no_reload_scope_depth_ == 0);
1056 #else
1057 return false;
1058 #endif
1059 }
1060
1061
1062 #ifndef PRODUCT
1063 void Isolate::ReportReloadError(const Error& error) {
1064 ASSERT(IsReloading());
1065 reload_context_->AbortReload(error);
1066 delete reload_context_;
1067 reload_context_ = NULL;
1068 }
1069
1070
1071 void Isolate::OnStackReload() {
1072 UNREACHABLE();
1073 }
1074
1075
1076 void Isolate::ReloadSources(bool test_mode) {
1077 ASSERT(!IsReloading());
1078 has_attempted_reload_ = true;
1079 reload_context_ = new IsolateReloadContext(this, test_mode);
1080 reload_context_->StartReload();
1081 }
1082
1083 #endif
1084
1085
1086 void Isolate::DoneFinalizing() {
1087 NOT_IN_PRODUCT(
1088 if (IsReloading()) {
1089 reload_context_->FinishReload();
1090 if (reload_context_->has_error() && reload_context_->test_mode()) {
1091 // If the reload has an error and we are in test mode keep the reload
1092 // context on the isolate so that it can be used by unit tests.
1093 return;
1094 }
1095 if (!reload_context_->has_error()) {
1096 reload_context_->ReportSuccess();
1097 }
1098 delete reload_context_;
1099 reload_context_ = NULL;
1100 }
1101 )
1102 }
1103
1104
1105
1019 bool Isolate::MakeRunnable() { 1106 bool Isolate::MakeRunnable() {
1020 ASSERT(Isolate::Current() == NULL); 1107 ASSERT(Isolate::Current() == NULL);
1021 1108
1022 MutexLocker ml(mutex_); 1109 MutexLocker ml(mutex_);
1023 // Check if we are in a valid state to make the isolate runnable. 1110 // Check if we are in a valid state to make the isolate runnable.
1024 if (is_runnable() == true) { 1111 if (is_runnable() == true) {
1025 return false; // Already runnable. 1112 return false; // Already runnable.
1026 } 1113 }
1027 // Set the isolate as runnable and if we are being spawned schedule 1114 // Set the isolate as runnable and if we are being spawned schedule
1028 // isolate on thread pool for execution. 1115 // isolate on thread pool for execution.
(...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after
1683 // 'disabling_field_list_' access via mutator and background compilation 1770 // 'disabling_field_list_' access via mutator and background compilation
1684 // threads is guarded with a monitor. This means that we can visit it only 1771 // threads is guarded with a monitor. This means that we can visit it only
1685 // when at safepoint or the field_list_mutex_ lock has been taken. 1772 // when at safepoint or the field_list_mutex_ lock has been taken.
1686 visitor->VisitPointer(reinterpret_cast<RawObject**>(&disabling_field_list_)); 1773 visitor->VisitPointer(reinterpret_cast<RawObject**>(&disabling_field_list_));
1687 1774
1688 // Visit objects in the debugger. 1775 // Visit objects in the debugger.
1689 if (FLAG_support_debugger) { 1776 if (FLAG_support_debugger) {
1690 debugger()->VisitObjectPointers(visitor); 1777 debugger()->VisitObjectPointers(visitor);
1691 } 1778 }
1692 1779
1780 NOT_IN_PRODUCT(
1781 // Visit objects that are being used for isolate reload.
1782 if (reload_context() != NULL) {
1783 reload_context()->VisitObjectPointers(visitor);
1784 }
1785 )
1786
1693 // Visit objects that are being used for deoptimization. 1787 // Visit objects that are being used for deoptimization.
1694 if (deopt_context() != NULL) { 1788 if (deopt_context() != NULL) {
1695 deopt_context()->VisitObjectPointers(visitor); 1789 deopt_context()->VisitObjectPointers(visitor);
1696 } 1790 }
1697 1791
1698 // Visit objects in all threads (e.g., Dart stack, handles in zones). 1792 // Visit objects in all threads (e.g., Dart stack, handles in zones).
1699 thread_registry()->VisitObjectPointers(visitor, validate_frames); 1793 thread_registry()->VisitObjectPointers(visitor, validate_frames);
1700 } 1794 }
1701 1795
1702 1796
1703 void Isolate::VisitWeakPersistentHandles(HandleVisitor* visitor) { 1797 void Isolate::VisitWeakPersistentHandles(HandleVisitor* visitor) {
1704 if (api_state() != NULL) { 1798 if (api_state() != NULL) {
1705 api_state()->VisitWeakHandles(visitor); 1799 api_state()->VisitWeakHandles(visitor);
1706 } 1800 }
1707 } 1801 }
1708 1802
1709 1803
1710 void Isolate::PrepareForGC() { 1804 void Isolate::PrepareForGC() {
1711 thread_registry()->PrepareForGC(); 1805 thread_registry()->PrepareForGC();
1712 } 1806 }
1713 1807
1714 1808
1809 RawClass* Isolate::GetClassForHeapWalkAt(intptr_t cid) {
1810 RawClass* raw_class = NULL;
1811 #ifndef PRODUCT
1812 if (IsReloading()) {
1813 raw_class = reload_context()->GetClassForHeapWalkAt(cid);
1814 } else {
1815 raw_class = class_table()->At(cid);
1816 }
1817 #else
1818 raw_class = class_table()->At(cid);
1819 #endif // !PRODUCT
1820 ASSERT(raw_class != NULL);
1821 ASSERT(raw_class->ptr()->id_ == cid);
1822 return raw_class;
1823 }
1824
1825
1715 static const char* ExceptionPauseInfoToServiceEnum(Dart_ExceptionPauseInfo pi) { 1826 static const char* ExceptionPauseInfoToServiceEnum(Dart_ExceptionPauseInfo pi) {
1716 switch (pi) { 1827 switch (pi) {
1717 case kPauseOnAllExceptions: 1828 case kPauseOnAllExceptions:
1718 return "All"; 1829 return "All";
1719 case kNoPauseOnExceptions: 1830 case kNoPauseOnExceptions:
1720 return "None"; 1831 return "None";
1721 case kPauseOnUnhandledExceptions: 1832 case kPauseOnUnhandledExceptions:
1722 return "Unhandled"; 1833 return "Unhandled";
1723 default: 1834 default:
1724 UNIMPLEMENTED(); 1835 UNIMPLEMENTED();
(...skipping 23 matching lines...) Expand all
1748 jsobj.AddPropertyTimeMillis("startTime", start_time_millis); 1859 jsobj.AddPropertyTimeMillis("startTime", start_time_millis);
1749 { 1860 {
1750 JSONObject jsheap(&jsobj, "_heaps"); 1861 JSONObject jsheap(&jsobj, "_heaps");
1751 heap()->PrintToJSONObject(Heap::kNew, &jsheap); 1862 heap()->PrintToJSONObject(Heap::kNew, &jsheap);
1752 heap()->PrintToJSONObject(Heap::kOld, &jsheap); 1863 heap()->PrintToJSONObject(Heap::kOld, &jsheap);
1753 } 1864 }
1754 1865
1755 jsobj.AddProperty("runnable", is_runnable()); 1866 jsobj.AddProperty("runnable", is_runnable());
1756 jsobj.AddProperty("livePorts", message_handler()->live_ports()); 1867 jsobj.AddProperty("livePorts", message_handler()->live_ports());
1757 jsobj.AddProperty("pauseOnExit", message_handler()->should_pause_on_exit()); 1868 jsobj.AddProperty("pauseOnExit", message_handler()->should_pause_on_exit());
1869 jsobj.AddProperty("_isReloading", IsReloading());
1758 1870
1759 if (debugger() != NULL) { 1871 if (debugger() != NULL) {
1760 if (!is_runnable()) { 1872 if (!is_runnable()) {
1761 // Isolate is not yet runnable. 1873 // Isolate is not yet runnable.
1762 ASSERT(debugger()->PauseEvent() == NULL); 1874 ASSERT(debugger()->PauseEvent() == NULL);
1763 ServiceEvent pause_event(this, ServiceEvent::kNone); 1875 ServiceEvent pause_event(this, ServiceEvent::kNone);
1764 jsobj.AddProperty("pauseEvent", &pause_event); 1876 jsobj.AddProperty("pauseEvent", &pause_event);
1765 } else if (message_handler()->is_paused_on_start() || 1877 } else if (message_handler()->is_paused_on_start() ||
1766 message_handler()->should_pause_on_start()) { 1878 message_handler()->should_pause_on_start()) {
1767 ASSERT(debugger()->PauseEvent() == NULL); 1879 ASSERT(debugger()->PauseEvent() == NULL);
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after
2180 if (pause_loop_monitor_ == NULL) { 2292 if (pause_loop_monitor_ == NULL) {
2181 pause_loop_monitor_ = new Monitor(); 2293 pause_loop_monitor_ = new Monitor();
2182 } 2294 }
2183 Dart_EnterScope(); 2295 Dart_EnterScope();
2184 MonitorLocker ml(pause_loop_monitor_); 2296 MonitorLocker ml(pause_loop_monitor_);
2185 2297
2186 Dart_MessageNotifyCallback saved_notify_callback = 2298 Dart_MessageNotifyCallback saved_notify_callback =
2187 message_notify_callback(); 2299 message_notify_callback();
2188 set_message_notify_callback(Isolate::WakePauseEventHandler); 2300 set_message_notify_callback(Isolate::WakePauseEventHandler);
2189 2301
2302 const bool had_isolate_reload_context = reload_context() != NULL;
2303 const int64_t start_time_micros =
2304 !had_isolate_reload_context ? 0 : reload_context()->start_time_micros();
2190 bool resume = false; 2305 bool resume = false;
2191 while (true) { 2306 while (true) {
2192 // Handle all available vm service messages, up to a resume 2307 // Handle all available vm service messages, up to a resume
2193 // request. 2308 // request.
2194 while (!resume && Dart_HasServiceMessages()) { 2309 while (!resume && Dart_HasServiceMessages()) {
2195 ml.Exit(); 2310 ml.Exit();
2196 resume = Dart_HandleServiceMessages(); 2311 resume = Dart_HandleServiceMessages();
2197 ml.Enter(); 2312 ml.Enter();
2198 } 2313 }
2199 if (resume) { 2314 if (resume) {
2200 break; 2315 break;
2201 } 2316 }
2202 2317
2318 if (had_isolate_reload_context && (reload_context() == NULL)) {
2319 if (FLAG_trace_reload) {
2320 const int64_t reload_time_micros =
2321 OS::GetCurrentMonotonicMicros() - start_time_micros;
2322 double reload_millis =
2323 MicrosecondsToMilliseconds(reload_time_micros);
2324 OS::Print("Reloading has finished! (%.2f ms)\n", reload_millis);
2325 }
2326 break;
2327 }
2328
2203 // Wait for more service messages. 2329 // Wait for more service messages.
2204 Monitor::WaitResult res = ml.Wait(); 2330 Monitor::WaitResult res = ml.Wait();
2205 ASSERT(res == Monitor::kNotified); 2331 ASSERT(res == Monitor::kNotified);
2206 } 2332 }
2207 set_message_notify_callback(saved_notify_callback); 2333 set_message_notify_callback(saved_notify_callback);
2208 Dart_ExitScope(); 2334 Dart_ExitScope();
2209 } 2335 }
2210 2336
2211 2337
2212 void Isolate::VisitIsolates(IsolateVisitor* visitor) { 2338 void Isolate::VisitIsolates(IsolateVisitor* visitor) {
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
2716 void IsolateSpawnState::DecrementSpawnCount() { 2842 void IsolateSpawnState::DecrementSpawnCount() {
2717 ASSERT(spawn_count_monitor_ != NULL); 2843 ASSERT(spawn_count_monitor_ != NULL);
2718 ASSERT(spawn_count_ != NULL); 2844 ASSERT(spawn_count_ != NULL);
2719 MonitorLocker ml(spawn_count_monitor_); 2845 MonitorLocker ml(spawn_count_monitor_);
2720 ASSERT(*spawn_count_ > 0); 2846 ASSERT(*spawn_count_ > 0);
2721 *spawn_count_ = *spawn_count_ - 1; 2847 *spawn_count_ = *spawn_count_ - 1;
2722 ml.Notify(); 2848 ml.Notify();
2723 } 2849 }
2724 2850
2725 } // namespace dart 2851 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698