diff --git a/runtime/vm/os_thread.cc b/runtime/vm/os_thread.cc index c615fb3cc0b..baf7921bbda 100644 --- a/runtime/vm/os_thread.cc +++ b/runtime/vm/os_thread.cc @@ -85,10 +85,17 @@ OSThread::~OSThread() { delete log_; log_ = nullptr; #if defined(SUPPORT_TIMELINE) - if (Timeline::recorder() != nullptr) { + RecorderSynchronizationLockScope ls; + TimelineEventRecorder* recorder = Timeline::recorder(); + if (recorder != nullptr && !ls.IsShuttingDown()) { // Acquire the recorder's lock so that |timeline_block_| cannot be given to // another thread until the call to |TimelineEventRecorder::FinishBlock| is - // complete. + // complete. This is guarded by a check ensuring that the recorder has not + // yet been deleted, and thus ensuring that the recorder's lock can be + // acquired. It is fine to skip the call to + // |TimelineEventRecorder::FinishBlock| if the recorder has already been + // deleted, because only the recorder cares about whether blocks have been + // marked as finished. MutexLocker recorder_lock_locker(&Timeline::recorder()->lock_); MutexLocker timeline_block_lock_locker(timeline_block_lock()); Timeline::recorder()->FinishBlock(timeline_block_); diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h index 778d02110db..1e3777eb1af 100644 --- a/runtime/vm/timeline.h +++ b/runtime/vm/timeline.h @@ -173,6 +173,10 @@ class RecorderSynchronizationLock : public AllStatic { return (recorder_state_.load(std::memory_order_acquire) == kActive); } + static bool IsShuttingDown() { + return (recorder_state_.load(std::memory_order_acquire) == kShuttingDown); + } + static void WaitForShutdown() { recorder_state_.store(kShuttingDown, std::memory_order_release); // Spin waiting for outstanding events to be completed. @@ -207,6 +211,10 @@ class RecorderSynchronizationLockScope { bool IsActive() const { return RecorderSynchronizationLock::IsActive(); } + bool IsShuttingDown() const { + return RecorderSynchronizationLock::IsShuttingDown(); + } + private: DISALLOW_COPY_AND_ASSIGN(RecorderSynchronizationLockScope); };