@@ -129,7 +129,8 @@ class CompilationStateImpl {
129129 void SetNumberOfFunctionsToCompile (int num_functions);
130130
131131 // Add the callback function to be called on compilation events. Needs to be
132- // set before {AddCompilationUnits} is run.
132+ // set before {AddCompilationUnits} is run to ensure that it receives all
133+ // events. The callback object must support being deleted from any thread.
133134 void AddCallback (CompilationState::callback_t );
134135
135136 // Inserts new functions to compile and kicks off compilation.
@@ -153,7 +154,7 @@ class CompilationStateImpl {
153154 }
154155
155156 bool baseline_compilation_finished () const {
156- base::MutexGuard guard (&mutex_ );
157+ base::MutexGuard guard (&callbacks_mutex_ );
157158 return outstanding_baseline_units_ == 0 ||
158159 (compile_mode_ == CompileMode::kTiering &&
159160 outstanding_tiering_units_ == 0 );
@@ -203,8 +204,6 @@ class CompilationStateImpl {
203204 : func_index(func_index), error(std::move(error)) {}
204205 };
205206
206- void NotifyOnEvent (CompilationEvent event);
207-
208207 NativeModule* const native_module_;
209208 const std::shared_ptr<BackgroundCompileToken> background_compile_token_;
210209 const CompileMode compile_mode_;
@@ -236,16 +235,26 @@ class CompilationStateImpl {
236235 // compiling.
237236 std::shared_ptr<WireBytesStorage> wire_bytes_storage_;
238237
239- int outstanding_baseline_units_ = 0 ;
240- int outstanding_tiering_units_ = 0 ;
241-
242238 // End of fields protected by {mutex_}.
243239 // ////////////////////////////////////////////////////////////////////////////
244240
245- // Callback functions to be called on compilation events. Only accessible from
246- // the foreground thread.
241+ // This mutex protects the callbacks vector, and the counters used to
242+ // determine which callbacks to call. The counters plus the callbacks
243+ // themselves need to be synchronized to ensure correct order of events.
244+ mutable base::Mutex callbacks_mutex_;
245+
246+ // ////////////////////////////////////////////////////////////////////////////
247+ // Protected by {callbacks_mutex_}:
248+
249+ // Callback functions to be called on compilation events.
247250 std::vector<CompilationState::callback_t > callbacks_;
248251
252+ int outstanding_baseline_units_ = 0 ;
253+ int outstanding_tiering_units_ = 0 ;
254+
255+ // End of fields protected by {callbacks_mutex_}.
256+ // ////////////////////////////////////////////////////////////////////////////
257+
249258 const int max_background_tasks_ = 0 ;
250259};
251260
@@ -852,6 +861,7 @@ std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
852861}
853862
854863AsyncCompileJob::~AsyncCompileJob () {
864+ // Note: This destructor always runs on the foreground thread of the isolate.
855865 background_task_manager_.CancelAndWait ();
856866 // If the runtime objects were not created yet, then initial compilation did
857867 // not finish yet. In this case we can abort compilation.
@@ -1473,12 +1483,13 @@ CompilationStateImpl::~CompilationStateImpl() {
14731483void CompilationStateImpl::AbortCompilation () {
14741484 background_compile_token_->Cancel ();
14751485 // No more callbacks after abort.
1486+ base::MutexGuard callbacks_guard (&callbacks_mutex_);
14761487 callbacks_.clear ();
14771488}
14781489
14791490void CompilationStateImpl::SetNumberOfFunctionsToCompile (int num_functions) {
14801491 DCHECK (!failed ());
1481- base::MutexGuard guard (&mutex_ );
1492+ base::MutexGuard guard (&callbacks_mutex_ );
14821493 outstanding_baseline_units_ = num_functions;
14831494
14841495 if (compile_mode_ == CompileMode::kTiering ) {
@@ -1487,6 +1498,7 @@ void CompilationStateImpl::SetNumberOfFunctionsToCompile(int num_functions) {
14871498}
14881499
14891500void CompilationStateImpl::AddCallback (CompilationState::callback_t callback) {
1501+ base::MutexGuard callbacks_guard (&callbacks_mutex_);
14901502 callbacks_.emplace_back (std::move (callback));
14911503}
14921504
@@ -1536,7 +1548,7 @@ CompilationStateImpl::GetNextCompilationUnit() {
15361548
15371549void CompilationStateImpl::OnFinishedUnit (ExecutionTier tier, WasmCode* code) {
15381550 // This mutex guarantees that events happen in the right order.
1539- base::MutexGuard guard (&mutex_ );
1551+ base::MutexGuard guard (&callbacks_mutex_ );
15401552
15411553 // If we are *not* compiling in tiering mode, then all units are counted as
15421554 // baseline units.
@@ -1547,28 +1559,36 @@ void CompilationStateImpl::OnFinishedUnit(ExecutionTier tier, WasmCode* code) {
15471559 // tiering units.
15481560 DCHECK_IMPLIES (!is_tiering_mode, outstanding_tiering_units_ == 0 );
15491561
1562+ bool baseline_finished = false ;
1563+ bool tiering_finished = false ;
15501564 if (is_tiering_unit) {
15511565 DCHECK_LT (0 , outstanding_tiering_units_);
15521566 --outstanding_tiering_units_;
1553- if (outstanding_tiering_units_ == 0 ) {
1554- // If baseline compilation has not finished yet, then also trigger
1555- // {kFinishedBaselineCompilation}.
1556- if (outstanding_baseline_units_ > 0 ) {
1557- NotifyOnEvent (CompilationEvent::kFinishedBaselineCompilation );
1558- }
1559- NotifyOnEvent (CompilationEvent::kFinishedTopTierCompilation );
1560- }
1567+ tiering_finished = outstanding_tiering_units_ == 0 ;
1568+ // If baseline compilation has not finished yet, then also trigger
1569+ // {kFinishedBaselineCompilation}.
1570+ baseline_finished = tiering_finished && outstanding_baseline_units_ > 0 ;
15611571 } else {
15621572 DCHECK_LT (0 , outstanding_baseline_units_);
15631573 --outstanding_baseline_units_;
1564- if (outstanding_baseline_units_ == 0 ) {
1565- NotifyOnEvent (CompilationEvent::kFinishedBaselineCompilation );
1566- // If we are not tiering, then we also trigger the "top tier finished"
1567- // event when baseline compilation is finished.
1568- if (!is_tiering_mode) {
1569- NotifyOnEvent (CompilationEvent::kFinishedTopTierCompilation );
1570- }
1571- }
1574+ // If we are in tiering mode and tiering finished before, then do not
1575+ // trigger baseline finished.
1576+ baseline_finished = outstanding_baseline_units_ == 0 &&
1577+ (!is_tiering_mode || outstanding_tiering_units_ > 0 );
1578+ // If we are not tiering, then we also trigger the "top tier finished"
1579+ // event when baseline compilation is finished.
1580+ tiering_finished = baseline_finished && !is_tiering_mode;
1581+ }
1582+
1583+ if (baseline_finished) {
1584+ for (auto & callback : callbacks_)
1585+ callback (CompilationEvent::kFinishedBaselineCompilation );
1586+ }
1587+ if (tiering_finished) {
1588+ for (auto & callback : callbacks_)
1589+ callback (CompilationEvent::kFinishedTopTierCompilation );
1590+ // Clear the callbacks because no more events will be delivered.
1591+ callbacks_.clear ();
15721592 }
15731593
15741594 if (code != nullptr ) native_module_->engine ()->LogCode (code);
@@ -1648,17 +1668,12 @@ void CompilationStateImpl::SetError(uint32_t func_index,
16481668 if (!set) return ;
16491669 // If set successfully, give up ownership.
16501670 compile_error.release ();
1651- // Schedule a foreground task to call the callback and notify users about the
1652- // compile error.
1653- NotifyOnEvent (CompilationEvent::kFailedCompilation );
1654- }
1655-
1656- void CompilationStateImpl::NotifyOnEvent (CompilationEvent event) {
1657- for (auto & callback : callbacks_) callback (event);
1658- // If no more events are expected after this one, clear the callbacks to free
1659- // memory. We can safely do this here, as this method is only called from
1660- // foreground tasks.
1661- if (event >= CompilationEvent::kFirstFinalEvent ) callbacks_.clear ();
1671+ base::MutexGuard callbacks_guard (&callbacks_mutex_);
1672+ for (auto & callback : callbacks_) {
1673+ callback (CompilationEvent::kFailedCompilation );
1674+ }
1675+ // No more callbacks after an error.
1676+ callbacks_.clear ();
16621677}
16631678
16641679void CompileJsToWasmWrappers (Isolate* isolate, const WasmModule* module ,
0 commit comments