Makes critsect_.Leave() more visible in PacedSender.

This means that the PacedSender::Process function becomes slightly
larger, however, it makes it much more obvious to the reader where
the locks are held or not. Confusion over this has previously caused
bugs.

Bug: webrtc:9870
Change-Id: I63257eae59ecf5e7dd28ea24f63157cefe9f81bd
Reviewed-on: https://webrtc-review.googlesource.com/c/105460
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25389}
diff --git a/modules/pacing/paced_sender.cc b/modules/pacing/paced_sender.cc
index 2fed7be..7e0ac78 100644
--- a/modules/pacing/paced_sender.cc
+++ b/modules/pacing/paced_sender.cc
@@ -11,6 +11,7 @@
 #include "modules/pacing/paced_sender.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "absl/memory/memory.h"
 #include "logging/rtc_event_log/rtc_event_log.h"
@@ -257,9 +258,7 @@
   return std::max<int64_t>(kMinPacketLimitMs - elapsed_time_ms, 0);
 }
 
-void PacedSender::Process() {
-  int64_t now_us = clock_->TimeInMicroseconds();
-  rtc::CritScope cs(&critsect_);
+int64_t PacedSender::UpdateTimeAndGetElapsedMs(int64_t now_us) {
   int64_t elapsed_time_ms = (now_us - time_last_process_us_ + 500) / 1000;
   time_last_process_us_ = now_us;
   if (elapsed_time_ms > kMaxElapsedTimeMs) {
@@ -268,6 +267,10 @@
                         << kMaxElapsedTimeMs << " ms";
     elapsed_time_ms = kMaxElapsedTimeMs;
   }
+  return elapsed_time_ms;
+}
+
+bool PacedSender::ShouldSendKeepalive(int64_t now_us) const {
   if (send_padding_if_silent_ || paused_ || Congested()) {
     // We send a padding packet every 500 ms to ensure we won't get stuck in
     // congested state due to no feedback being received.
@@ -276,12 +279,25 @@
       // We can not send padding unless a normal packet has first been sent. If
       // we do, timestamps get messed up.
       if (packet_counter_ > 0) {
-        PacedPacketInfo pacing_info;
-        size_t bytes_sent = SendPadding(1, pacing_info);
-        alr_detector_->OnBytesSent(bytes_sent, now_us / 1000);
+        return true;
       }
     }
   }
+  return false;
+}
+
+void PacedSender::Process() {
+  rtc::CritScope cs(&critsect_);
+  int64_t now_us = clock_->TimeInMicroseconds();
+  int64_t elapsed_time_ms = UpdateTimeAndGetElapsedMs(now_us);
+  if (ShouldSendKeepalive(now_us)) {
+    critsect_.Leave();
+    size_t bytes_sent = packet_sender_->TimeToSendPadding(1, PacedPacketInfo());
+    critsect_.Enter();
+    OnPaddingSent(bytes_sent);
+    alr_detector_->OnBytesSent(bytes_sent, now_us / 1000);
+  }
+
   if (paused_)
     return;
 
@@ -315,23 +331,27 @@
     pacing_info = prober_.CurrentCluster();
     recommended_probe_size = prober_.RecommendedMinProbeSize();
   }
-  // The paused state is checked in the loop since SendPacket leaves the
-  // critical section allowing the paused state to be changed from other code.
+  // The paused state is checked in the loop since it leaves the critical
+  // section allowing the paused state to be changed from other code.
   while (!packets_.Empty() && !paused_) {
-    // Since we need to release the lock in order to send, we first pop the
-    // element from the priority queue but keep it in storage, so that we can
-    // reinsert it if send fails.
-    const RoundRobinPacketQueue::Packet& packet = packets_.BeginPop();
+    const auto* packet = GetPendingPacket(pacing_info);
+    if (packet == nullptr)
+      break;
 
-    if (SendPacket(packet, pacing_info)) {
-      bytes_sent += packet.bytes;
+    critsect_.Leave();
+    bool success = packet_sender_->TimeToSendPacket(
+        packet->ssrc, packet->sequence_number, packet->capture_time_ms,
+        packet->retransmission, pacing_info);
+    critsect_.Enter();
+    if (success) {
+      bytes_sent += packet->bytes;
       // Send succeeded, remove it from the queue.
-      packets_.FinalizePop(packet);
+      OnPacketSent(std::move(packet));
       if (is_probing && bytes_sent > recommended_probe_size)
         break;
     } else {
       // Send failed, put it back into the queue.
-      packets_.CancelPop(packet);
+      packets_.CancelPop(*packet);
       break;
     }
   }
@@ -344,7 +364,12 @@
           static_cast<int>(is_probing ? (recommended_probe_size - bytes_sent)
                                       : padding_budget_.bytes_remaining());
       if (padding_needed > 0) {
-        bytes_sent += SendPadding(padding_needed, pacing_info);
+        critsect_.Leave();
+        size_t padding_sent =
+            packet_sender_->TimeToSendPadding(padding_needed, pacing_info);
+        critsect_.Enter();
+        bytes_sent += padding_sent;
+        OnPaddingSent(padding_sent);
       }
     }
   }
@@ -362,54 +387,46 @@
   process_thread_ = process_thread;
 }
 
-bool PacedSender::SendPacket(const RoundRobinPacketQueue::Packet& packet,
-                             const PacedPacketInfo& pacing_info) {
-  RTC_DCHECK(!paused_);
-  bool audio_packet = packet.priority == kHighPriority;
+const RoundRobinPacketQueue::Packet* PacedSender::GetPendingPacket(
+    const PacedPacketInfo& pacing_info) {
+  // Since we need to release the lock in order to send, we first pop the
+  // element from the priority queue but keep it in storage, so that we can
+  // reinsert it if send fails.
+  const RoundRobinPacketQueue::Packet* packet = &packets_.BeginPop();
+  bool audio_packet = packet->priority == kHighPriority;
   bool apply_pacing =
       !audio_packet || account_for_audio_ || video_blocks_audio_;
   if (apply_pacing && (Congested() || (media_budget_.bytes_remaining() == 0 &&
                                        pacing_info.probe_cluster_id ==
                                            PacedPacketInfo::kNotAProbe))) {
-    return false;
+    packets_.CancelPop(*packet);
+    return nullptr;
   }
-
-  critsect_.Leave();
-  const bool success = packet_sender_->TimeToSendPacket(
-      packet.ssrc, packet.sequence_number, packet.capture_time_ms,
-      packet.retransmission, pacing_info);
-  critsect_.Enter();
-
-  if (success) {
-    if (first_sent_packet_ms_ == -1)
-      first_sent_packet_ms_ = TimeMilliseconds();
-    if (!audio_packet || account_for_audio_) {
-      // Update media bytes sent.
-      // TODO(eladalon): TimeToSendPacket() can also return |true| in some
-      // situations where nothing actually ended up being sent to the network,
-      // and we probably don't want to update the budget in such cases.
-      // https://bugs.chromium.org/p/webrtc/issues/detail?id=8052
-      UpdateBudgetWithBytesSent(packet.bytes);
-      last_send_time_us_ = clock_->TimeInMicroseconds();
-    }
-  }
-
-  return success;
+  return packet;
 }
 
-size_t PacedSender::SendPadding(size_t padding_needed,
-                                const PacedPacketInfo& pacing_info) {
-  RTC_DCHECK_GT(packet_counter_, 0);
-  critsect_.Leave();
-  size_t bytes_sent =
-      packet_sender_->TimeToSendPadding(padding_needed, pacing_info);
-  critsect_.Enter();
+void PacedSender::OnPacketSent(const RoundRobinPacketQueue::Packet* packet) {
+  if (first_sent_packet_ms_ == -1)
+    first_sent_packet_ms_ = TimeMilliseconds();
+  bool audio_packet = packet->priority == kHighPriority;
+  if (!audio_packet || account_for_audio_) {
+    // Update media bytes sent.
+    // TODO(eladalon): TimeToSendPacket() can also return |true| in some
+    // situations where nothing actually ended up being sent to the network,
+    // and we probably don't want to update the budget in such cases.
+    // https://bugs.chromium.org/p/webrtc/issues/detail?id=8052
+    UpdateBudgetWithBytesSent(packet->bytes);
+    last_send_time_us_ = clock_->TimeInMicroseconds();
+  }
+  // Send succeeded, remove it from the queue.
+  packets_.FinalizePop(*packet);
+}
 
+void PacedSender::OnPaddingSent(size_t bytes_sent) {
   if (bytes_sent > 0) {
     UpdateBudgetWithBytesSent(bytes_sent);
   }
   last_send_time_us_ = clock_->TimeInMicroseconds();
-  return bytes_sent;
 }
 
 void PacedSender::UpdateBudgetWithElapsedTime(int64_t delta_time_ms) {
diff --git a/modules/pacing/paced_sender.h b/modules/pacing/paced_sender.h
index b6f294f..4586d29 100644
--- a/modules/pacing/paced_sender.h
+++ b/modules/pacing/paced_sender.h
@@ -143,19 +143,25 @@
   void SetQueueTimeLimit(int limit_ms);
 
  private:
+  int64_t UpdateTimeAndGetElapsedMs(int64_t now_us)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
+  bool ShouldSendKeepalive(int64_t at_time_us) const
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
+
   // Updates the number of bytes that can be sent for the next time interval.
   void UpdateBudgetWithElapsedTime(int64_t delta_time_in_ms)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
   void UpdateBudgetWithBytesSent(size_t bytes)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
 
-  bool SendPacket(const RoundRobinPacketQueue::Packet& packet,
-                  const PacedPacketInfo& cluster_info)
+  const RoundRobinPacketQueue::Packet* GetPendingPacket(
+      const PacedPacketInfo& pacing_info)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
-  size_t SendPadding(size_t padding_needed, const PacedPacketInfo& cluster_info)
+  void OnPacketSent(const RoundRobinPacketQueue::Packet* packet)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
+  void OnPaddingSent(size_t padding_sent)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
 
-  void OnBytesSent(size_t bytes_sent) RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
   bool Congested() const RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);
   int64_t TimeMilliseconds() const RTC_EXCLUSIVE_LOCKS_REQUIRED(critsect_);