Fix for crash in event log when using scenario tests.

Scenario tests runs all its activities on task queues. This is not
allowed by the default event log writer, causing a DCHECK failure.
This CL makes it possible to stop the event asynchronously,
thereby avoiding the need for the DCHECK.

Bug: webrtc:10365
Change-Id: I1206982b29fd609ac85b4ce30ae9291cbec52041
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/136685
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28027}
diff --git a/api/rtc_event_log/rtc_event_log.h b/api/rtc_event_log/rtc_event_log.h
index 5839f4b..05589b1 100644
--- a/api/rtc_event_log/rtc_event_log.h
+++ b/api/rtc_event_log/rtc_event_log.h
@@ -54,6 +54,15 @@
   // which it would be permissible to read and/or modify it.
   virtual void StopLogging() = 0;
 
+  // Stops logging to file and calls |callback| when the file has been closed.
+  // Note that it is not safe to call any other members, including the
+  // destructor, until the callback has been called.
+  // TODO(srte): Remove default implementation when it's safe to do so.
+  virtual void StopLogging(std::function<void()> callback) {
+    StopLogging();
+    callback();
+  }
+
   // Log an RTC event (the type of event is determined by the subclass).
   virtual void Log(std::unique_ptr<RtcEvent> event) = 0;
 };
diff --git a/logging/rtc_event_log/rtc_event_log_impl.cc b/logging/rtc_event_log/rtc_event_log_impl.cc
index 38e2ee7..7881190 100644
--- a/logging/rtc_event_log/rtc_event_log_impl.cc
+++ b/logging/rtc_event_log/rtc_event_log_impl.cc
@@ -82,6 +82,7 @@
       num_config_events_written_(0),
       last_output_ms_(rtc::TimeMillis()),
       output_scheduled_(false),
+      logging_state_started_(false),
       task_queue_(
           absl::make_unique<rtc::TaskQueue>(task_queue_factory->CreateTaskQueue(
               "rtc_event_log",
@@ -89,7 +90,8 @@
 
 RtcEventLogImpl::~RtcEventLogImpl() {
   // If we're logging to the output, this will stop that. Blocking function.
-  StopLogging();
+  if (logging_state_started_)
+    StopLogging();
 
   // We want to block on any executing task by invoking ~TaskQueue() before
   // we set unique_ptr's internal pointer to null.
@@ -125,6 +127,9 @@
     LogEventsFromMemoryToOutput();
   };
 
+  RTC_DCHECK_RUN_ON(&logging_state_checker_);
+  logging_state_started_ = true;
+
   task_queue_->PostTask(
       absl::make_unique<ResourceOwningTask<RtcEventLogOutput>>(
           std::move(output), start));
@@ -136,17 +141,7 @@
   RTC_LOG(LS_INFO) << "Stopping WebRTC event log.";
 
   rtc::Event output_stopped;
-
-  // Binding to |this| is safe because |this| outlives the |task_queue_|.
-  task_queue_->PostTask([this, &output_stopped]() {
-    RTC_DCHECK_RUN_ON(task_queue_.get());
-    if (event_output_) {
-      RTC_DCHECK(event_output_->IsActive());
-      LogEventsFromMemoryToOutput();
-    }
-    StopLoggingInternal();
-    output_stopped.Set();
-  });
+  StopLogging([&output_stopped]() { output_stopped.Set(); });
 
   // By making sure StopLogging() is not executed on a task queue,
   // we ensure it's not running on a thread that is shared with |task_queue_|,
@@ -158,6 +153,20 @@
   RTC_LOG(LS_INFO) << "WebRTC event log successfully stopped.";
 }
 
+void RtcEventLogImpl::StopLogging(std::function<void()> callback) {
+  RTC_DCHECK_RUN_ON(&logging_state_checker_);
+  logging_state_started_ = false;
+  task_queue_->PostTask([this, callback] {
+    RTC_DCHECK_RUN_ON(task_queue_.get());
+    if (event_output_) {
+      RTC_DCHECK(event_output_->IsActive());
+      LogEventsFromMemoryToOutput();
+    }
+    StopLoggingInternal();
+    callback();
+  });
+}
+
 void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) {
   RTC_CHECK(event);
 
diff --git a/logging/rtc_event_log/rtc_event_log_impl.h b/logging/rtc_event_log/rtc_event_log_impl.h
index e1ba830..160c112 100644
--- a/logging/rtc_event_log/rtc_event_log_impl.h
+++ b/logging/rtc_event_log/rtc_event_log_impl.h
@@ -43,6 +43,7 @@
   bool StartLogging(std::unique_ptr<RtcEventLogOutput> output,
                     int64_t output_period_ms) override;
   void StopLogging() override;
+  void StopLogging(std::function<void()> callback) override;
 
   void Log(std::unique_ptr<RtcEvent> event) override;
 
@@ -80,6 +81,9 @@
   int64_t last_output_ms_ RTC_GUARDED_BY(*task_queue_);
   bool output_scheduled_ RTC_GUARDED_BY(*task_queue_);
 
+  SequenceChecker logging_state_checker_;
+  bool logging_state_started_ RTC_GUARDED_BY(logging_state_checker_);
+
   // Since we are posting tasks bound to |this|,  it is critical that the event
   // log and its members outlive |task_queue_|. Keep the |task_queue_|
   // last to ensure it destructs first, or else tasks living on the queue might
diff --git a/test/scenario/BUILD.gn b/test/scenario/BUILD.gn
index 31c0db0..f136245 100644
--- a/test/scenario/BUILD.gn
+++ b/test/scenario/BUILD.gn
@@ -173,6 +173,7 @@
       "../../system_wrappers:field_trial",
       "../../test:field_trial",
       "../../test:test_support",
+      "../logging:log_writer",
       "//testing/gmock",
       "//third_party/abseil-cpp/absl/memory",
     ]
diff --git a/test/scenario/call_client.cc b/test/scenario/call_client.cc
index a710ad1..a4607bc 100644
--- a/test/scenario/call_client.cc
+++ b/test/scenario/call_client.cc
@@ -150,6 +150,9 @@
   SendTask([&] {
     call_.reset();
     fake_audio_setup_ = {};
+    rtc::Event done;
+    event_log_->StopLogging([&done] { done.Set(); });
+    done.Wait(rtc::Event::kForever);
     event_log_.reset();
   });
 }
diff --git a/test/scenario/scenario_unittest.cc b/test/scenario/scenario_unittest.cc
index eea8d59..1a0e1ff 100644
--- a/test/scenario/scenario_unittest.cc
+++ b/test/scenario/scenario_unittest.cc
@@ -10,6 +10,7 @@
 #include <atomic>
 
 #include "test/gtest.h"
+#include "test/logging/memory_log_writer.h"
 #include "test/scenario/scenario.h"
 #include "test/scenario/stats_collection.h"
 
@@ -132,5 +133,17 @@
   s.RunFor(TimeDelta::seconds(10));
 }
 
+TEST(ScenarioTest, WritesToRtcEventLog) {
+  MemoryLogStorage storage;
+  {
+    Scenario s(storage.CreateFactory(), false);
+    SetupVideoCall(s, nullptr);
+    s.RunFor(TimeDelta::seconds(1));
+  }
+  auto logs = storage.logs();
+  // We expect that a rtc event log has been created and that it has some data.
+  EXPECT_GE(storage.logs().at("alice.rtc.dat").size(), 1u);
+}
+
 }  // namespace test
 }  // namespace webrtc