2014-05-13 18:00:26 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
|
|
|
|
* in the file PATENTS. All contributing project authors may
|
|
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-07-05 19:08:33 +02:00
|
|
|
#include "rtc_base/thread.h"
|
|
|
|
|
|
2025-01-17 13:19:45 +00:00
|
|
|
#include <atomic>
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
2016-05-01 14:53:46 -07:00
|
|
|
#include <memory>
|
2025-01-17 13:19:45 +00:00
|
|
|
#include <utility>
|
|
|
|
|
#include <vector>
|
2016-05-01 14:53:46 -07:00
|
|
|
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "absl/strings/string_view.h"
|
2022-08-23 12:57:16 +02:00
|
|
|
#include "api/field_trials_view.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "api/task_queue/task_queue_base.h"
|
2019-11-22 15:52:40 +01:00
|
|
|
#include "api/task_queue/task_queue_factory.h"
|
|
|
|
|
#include "api/task_queue/task_queue_test.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "api/test/rtc_error_matchers.h"
|
2022-07-06 19:42:34 +02:00
|
|
|
#include "api/units/time_delta.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "rtc_base/async_packet_socket.h"
|
2019-01-11 09:11:00 -08:00
|
|
|
#include "rtc_base/async_udp_socket.h"
|
2021-07-30 13:57:25 +02:00
|
|
|
#include "rtc_base/checks.h"
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "rtc_base/event.h"
|
2022-08-24 12:19:46 +02:00
|
|
|
#include "rtc_base/fake_clock.h"
|
2021-01-15 10:41:01 +01:00
|
|
|
#include "rtc_base/internal/default_socket_server.h"
|
2023-12-14 13:09:02 +01:00
|
|
|
#include "rtc_base/network/received_packet.h"
|
2019-01-11 09:11:00 -08:00
|
|
|
#include "rtc_base/null_socket_server.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "rtc_base/socket.h"
|
2019-01-11 09:11:00 -08:00
|
|
|
#include "rtc_base/socket_address.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "rtc_base/socket_server.h"
|
2020-07-10 13:23:25 +02:00
|
|
|
#include "rtc_base/synchronization/mutex.h"
|
2018-07-25 15:04:28 +02:00
|
|
|
#include "rtc_base/third_party/sigslot/sigslot.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "rtc_base/thread_annotations.h"
|
|
|
|
|
#include "rtc_base/time_utils.h"
|
2022-08-24 12:19:46 +02:00
|
|
|
#include "test/gmock.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "test/gtest.h"
|
2020-03-03 10:48:05 +01:00
|
|
|
#include "test/testsupport/rtc_expect_death.h"
|
2025-01-17 13:19:45 +00:00
|
|
|
#include "test/wait_until.h"
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
#if defined(WEBRTC_WIN)
|
|
|
|
|
#include <comdef.h> // NOLINT
|
2020-07-10 13:23:25 +02:00
|
|
|
|
2014-05-13 18:00:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
2018-12-11 18:43:40 +01:00
|
|
|
namespace rtc {
|
|
|
|
|
namespace {
|
2014-05-13 18:00:26 +00:00
|
|
|
|
2022-08-24 12:19:46 +02:00
|
|
|
using ::testing::ElementsAre;
|
2022-07-06 19:42:34 +02:00
|
|
|
using ::webrtc::TimeDelta;
|
2020-01-16 11:15:35 +01:00
|
|
|
|
2014-05-13 18:00:26 +00:00
|
|
|
// Generates a sequence of numbers (collaboratively).
|
|
|
|
|
class TestGenerator {
|
|
|
|
|
public:
|
|
|
|
|
TestGenerator() : last(0), count(0) {}
|
|
|
|
|
|
|
|
|
|
int Next(int prev) {
|
|
|
|
|
int result = prev + last;
|
|
|
|
|
last = result;
|
|
|
|
|
count += 1;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int last;
|
|
|
|
|
int count;
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-05 11:27:57 +02:00
|
|
|
// Receives messages and sends on a socket.
|
|
|
|
|
class MessageClient : public TestGenerator {
|
|
|
|
|
public:
|
|
|
|
|
MessageClient(Thread* pth, Socket* socket) : socket_(socket) {}
|
|
|
|
|
|
|
|
|
|
~MessageClient() { delete socket_; }
|
|
|
|
|
|
|
|
|
|
void OnValue(int value) {
|
|
|
|
|
int result = Next(value);
|
|
|
|
|
EXPECT_GE(socket_->Send(&result, sizeof(result)), 0);
|
|
|
|
|
}
|
2014-05-13 18:00:26 +00:00
|
|
|
|
2022-09-05 11:27:57 +02:00
|
|
|
private:
|
|
|
|
|
Socket* socket_;
|
2014-05-13 18:00:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Receives on a socket and sends by posting messages.
|
|
|
|
|
class SocketClient : public TestGenerator, public sigslot::has_slots<> {
|
|
|
|
|
public:
|
2021-08-12 10:32:30 +02:00
|
|
|
SocketClient(Socket* socket,
|
2018-06-19 15:03:05 +02:00
|
|
|
const SocketAddress& addr,
|
|
|
|
|
Thread* post_thread,
|
2022-09-05 11:27:57 +02:00
|
|
|
MessageClient* phandler)
|
2014-05-13 18:00:26 +00:00
|
|
|
: socket_(AsyncUDPSocket::Create(socket, addr)),
|
|
|
|
|
post_thread_(post_thread),
|
|
|
|
|
post_handler_(phandler) {
|
2023-12-14 13:09:02 +01:00
|
|
|
socket_->RegisterReceivedPacketCallback(
|
|
|
|
|
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
|
|
|
|
OnPacket(socket, packet);
|
|
|
|
|
});
|
2014-05-13 18:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
2017-10-24 10:08:26 -07:00
|
|
|
~SocketClient() override { delete socket_; }
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
SocketAddress address() const { return socket_->GetLocalAddress(); }
|
|
|
|
|
|
2023-12-14 13:09:02 +01:00
|
|
|
void OnPacket(AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
|
|
|
|
EXPECT_EQ(packet.payload().size(), sizeof(uint32_t));
|
|
|
|
|
uint32_t prev =
|
|
|
|
|
reinterpret_cast<const uint32_t*>(packet.payload().data())[0];
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
uint32_t result = Next(prev);
|
2014-05-13 18:00:26 +00:00
|
|
|
|
2022-09-05 11:27:57 +02:00
|
|
|
post_thread_->PostDelayedTask([post_handler_ = post_handler_,
|
|
|
|
|
result] { post_handler_->OnValue(result); },
|
|
|
|
|
TimeDelta::Millis(200));
|
2014-05-13 18:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
AsyncUDPSocket* socket_;
|
|
|
|
|
Thread* post_thread_;
|
2022-09-05 11:27:57 +02:00
|
|
|
MessageClient* post_handler_;
|
2014-05-13 18:00:26 +00:00
|
|
|
};
|
|
|
|
|
|
2017-05-23 12:55:03 -07:00
|
|
|
class CustomThread : public rtc::Thread {
|
2014-05-13 18:00:26 +00:00
|
|
|
public:
|
2017-07-14 14:44:46 -07:00
|
|
|
CustomThread()
|
|
|
|
|
: Thread(std::unique_ptr<SocketServer>(new rtc::NullSocketServer())) {}
|
2017-10-24 10:08:26 -07:00
|
|
|
~CustomThread() override { Stop(); }
|
2014-05-13 18:00:26 +00:00
|
|
|
bool Start() { return false; }
|
2014-09-18 16:45:21 +00:00
|
|
|
|
2018-06-19 15:03:05 +02:00
|
|
|
bool WrapCurrent() { return Thread::WrapCurrent(); }
|
|
|
|
|
void UnwrapCurrent() { Thread::UnwrapCurrent(); }
|
2014-05-13 18:00:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A thread that does nothing when it runs and signals an event
|
|
|
|
|
// when it is destroyed.
|
|
|
|
|
class SignalWhenDestroyedThread : public Thread {
|
|
|
|
|
public:
|
|
|
|
|
SignalWhenDestroyedThread(Event* event)
|
2017-07-14 14:44:46 -07:00
|
|
|
: Thread(std::unique_ptr<SocketServer>(new NullSocketServer())),
|
|
|
|
|
event_(event) {}
|
2014-05-13 18:00:26 +00:00
|
|
|
|
2017-10-24 10:08:26 -07:00
|
|
|
~SignalWhenDestroyedThread() override {
|
2014-05-13 18:00:26 +00:00
|
|
|
Stop();
|
|
|
|
|
event_->Set();
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-24 10:08:26 -07:00
|
|
|
void Run() override {
|
2014-05-13 18:00:26 +00:00
|
|
|
// Do nothing.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Event* event_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// See: https://code.google.com/p/webrtc/issues/detail?id=2409
|
|
|
|
|
TEST(ThreadTest, DISABLED_Main) {
|
2022-09-05 11:27:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2014-05-13 18:00:26 +00:00
|
|
|
const SocketAddress addr("127.0.0.1", 0);
|
|
|
|
|
|
|
|
|
|
// Create the messaging client on its own thread.
|
2017-07-14 14:44:46 -07:00
|
|
|
auto th1 = Thread::CreateWithSocketServer();
|
2021-08-12 10:32:30 +02:00
|
|
|
Socket* socket = th1->socketserver()->CreateSocket(addr.family(), SOCK_DGRAM);
|
2017-07-14 14:44:46 -07:00
|
|
|
MessageClient msg_client(th1.get(), socket);
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
// Create the socket client on its own thread.
|
2017-07-14 14:44:46 -07:00
|
|
|
auto th2 = Thread::CreateWithSocketServer();
|
2021-08-12 10:32:30 +02:00
|
|
|
Socket* asocket =
|
|
|
|
|
th2->socketserver()->CreateSocket(addr.family(), SOCK_DGRAM);
|
2017-07-14 14:44:46 -07:00
|
|
|
SocketClient sock_client(asocket, addr, th1.get(), &msg_client);
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
socket->Connect(sock_client.address());
|
|
|
|
|
|
2017-07-14 14:44:46 -07:00
|
|
|
th1->Start();
|
|
|
|
|
th2->Start();
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
// Get the messages started.
|
2022-09-05 11:27:57 +02:00
|
|
|
th1->PostDelayedTask([&msg_client] { msg_client.OnValue(1); },
|
|
|
|
|
TimeDelta::Millis(100));
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
// Give the clients a little while to run.
|
|
|
|
|
// Messages will be processed at 100, 300, 500, 700, 900.
|
|
|
|
|
Thread* th_main = Thread::Current();
|
|
|
|
|
th_main->ProcessMessages(1000);
|
|
|
|
|
|
|
|
|
|
// Stop the sending client. Give the receiver a bit longer to run, in case
|
|
|
|
|
// it is running on a machine that is under load (e.g. the build machine).
|
2017-07-14 14:44:46 -07:00
|
|
|
th1->Stop();
|
2014-05-13 18:00:26 +00:00
|
|
|
th_main->ProcessMessages(200);
|
2017-07-14 14:44:46 -07:00
|
|
|
th2->Stop();
|
2014-05-13 18:00:26 +00:00
|
|
|
|
|
|
|
|
// Make sure the results were correct
|
|
|
|
|
EXPECT_EQ(5, msg_client.count);
|
|
|
|
|
EXPECT_EQ(34, msg_client.last);
|
|
|
|
|
EXPECT_EQ(5, sock_client.count);
|
|
|
|
|
EXPECT_EQ(55, sock_client.last);
|
|
|
|
|
}
|
|
|
|
|
|
Add utility to count the number of blocking thread invokes.
This is useful to understand how often we block in certain parts of the
api and track improvements/regressions.
There are two macros, both are only active for RTC_DCHECK_IS_ON builds:
* RTC_LOG_THREAD_BLOCK_COUNT()
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
}
When executing this function during a test, the output could be:
(my_file.cc:2): Blocking MyFunction: total=1 (actual=1, would=0)
The words 'actual' and 'would' reflect whether an actual thread switch
was made, or if in the case of a test using the same thread for more
than one role (e.g. signaling, worker, network are all the same thread)
that an actual thread switch did not occur but it would have occurred
in the case of having dedicated threads. The 'total' count is the sum.
* RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x)
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
thread_->Invoke<void>([this](){ MoreStuff(); });
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
}
When a function is known to have blocking calls and we want to not
regress from the currently known number of blocking calls, we can use
this macro to state that at a certain point in a function, below
where RTC_LOG_THREAD_BLOCK_COUNT() is called, there must have occurred
no more than |x| (total) blocking calls. If more occur, a DCHECK will
hit and print out what the actual number of calls was:
# Fatal error in: my_file.cc, line 5
# last system error: 60
# Check failed: blocked_call_count_printer.GetTotalBlockedCallCount() <= 1 (2 vs. 1)
Bug: webrtc:12649
Change-Id: Ibac4f85f00b89680601dba54a651eac95a0f45d3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213782
Commit-Queue: Tommi <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33632}
2021-04-07 10:08:28 +02:00
|
|
|
TEST(ThreadTest, CountBlockingCalls) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread current;
|
|
|
|
|
|
Add utility to count the number of blocking thread invokes.
This is useful to understand how often we block in certain parts of the
api and track improvements/regressions.
There are two macros, both are only active for RTC_DCHECK_IS_ON builds:
* RTC_LOG_THREAD_BLOCK_COUNT()
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
}
When executing this function during a test, the output could be:
(my_file.cc:2): Blocking MyFunction: total=1 (actual=1, would=0)
The words 'actual' and 'would' reflect whether an actual thread switch
was made, or if in the case of a test using the same thread for more
than one role (e.g. signaling, worker, network are all the same thread)
that an actual thread switch did not occur but it would have occurred
in the case of having dedicated threads. The 'total' count is the sum.
* RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x)
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
thread_->Invoke<void>([this](){ MoreStuff(); });
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
}
When a function is known to have blocking calls and we want to not
regress from the currently known number of blocking calls, we can use
this macro to state that at a certain point in a function, below
where RTC_LOG_THREAD_BLOCK_COUNT() is called, there must have occurred
no more than |x| (total) blocking calls. If more occur, a DCHECK will
hit and print out what the actual number of calls was:
# Fatal error in: my_file.cc, line 5
# last system error: 60
# Check failed: blocked_call_count_printer.GetTotalBlockedCallCount() <= 1 (2 vs. 1)
Bug: webrtc:12649
Change-Id: Ibac4f85f00b89680601dba54a651eac95a0f45d3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213782
Commit-Queue: Tommi <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33632}
2021-04-07 10:08:28 +02:00
|
|
|
// When the test runs, this will print out:
|
|
|
|
|
// (thread_unittest.cc:262): Blocking TestBody: total=2 (actual=1, could=1)
|
|
|
|
|
RTC_LOG_THREAD_BLOCK_COUNT();
|
|
|
|
|
#if RTC_DCHECK_IS_ON
|
|
|
|
|
rtc::Thread::ScopedCountBlockingCalls blocked_calls(
|
|
|
|
|
[&](uint32_t actual_block, uint32_t could_block) {
|
|
|
|
|
EXPECT_EQ(1u, actual_block);
|
|
|
|
|
EXPECT_EQ(1u, could_block);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(0u, blocked_calls.GetBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(0u, blocked_calls.GetCouldBeBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(0u, blocked_calls.GetTotalBlockedCallCount());
|
|
|
|
|
|
|
|
|
|
// Test invoking on the current thread. This should not count as an 'actual'
|
|
|
|
|
// invoke, but should still count as an invoke that could block since we
|
2022-09-08 13:13:53 +02:00
|
|
|
// that the call to `BlockingCall` serves a purpose in some configurations
|
|
|
|
|
// (and should not be used a general way to call methods on the same thread).
|
|
|
|
|
current.BlockingCall([]() {});
|
Add utility to count the number of blocking thread invokes.
This is useful to understand how often we block in certain parts of the
api and track improvements/regressions.
There are two macros, both are only active for RTC_DCHECK_IS_ON builds:
* RTC_LOG_THREAD_BLOCK_COUNT()
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
}
When executing this function during a test, the output could be:
(my_file.cc:2): Blocking MyFunction: total=1 (actual=1, would=0)
The words 'actual' and 'would' reflect whether an actual thread switch
was made, or if in the case of a test using the same thread for more
than one role (e.g. signaling, worker, network are all the same thread)
that an actual thread switch did not occur but it would have occurred
in the case of having dedicated threads. The 'total' count is the sum.
* RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x)
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
thread_->Invoke<void>([this](){ MoreStuff(); });
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
}
When a function is known to have blocking calls and we want to not
regress from the currently known number of blocking calls, we can use
this macro to state that at a certain point in a function, below
where RTC_LOG_THREAD_BLOCK_COUNT() is called, there must have occurred
no more than |x| (total) blocking calls. If more occur, a DCHECK will
hit and print out what the actual number of calls was:
# Fatal error in: my_file.cc, line 5
# last system error: 60
# Check failed: blocked_call_count_printer.GetTotalBlockedCallCount() <= 1 (2 vs. 1)
Bug: webrtc:12649
Change-Id: Ibac4f85f00b89680601dba54a651eac95a0f45d3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213782
Commit-Queue: Tommi <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33632}
2021-04-07 10:08:28 +02:00
|
|
|
EXPECT_EQ(0u, blocked_calls.GetBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(1u, blocked_calls.GetCouldBeBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(1u, blocked_calls.GetTotalBlockedCallCount());
|
|
|
|
|
|
|
|
|
|
// Create a new thread to invoke on.
|
|
|
|
|
auto thread = Thread::CreateWithSocketServer();
|
|
|
|
|
thread->Start();
|
2022-09-08 13:13:53 +02:00
|
|
|
EXPECT_EQ(42, thread->BlockingCall([]() { return 42; }));
|
Add utility to count the number of blocking thread invokes.
This is useful to understand how often we block in certain parts of the
api and track improvements/regressions.
There are two macros, both are only active for RTC_DCHECK_IS_ON builds:
* RTC_LOG_THREAD_BLOCK_COUNT()
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
}
When executing this function during a test, the output could be:
(my_file.cc:2): Blocking MyFunction: total=1 (actual=1, would=0)
The words 'actual' and 'would' reflect whether an actual thread switch
was made, or if in the case of a test using the same thread for more
than one role (e.g. signaling, worker, network are all the same thread)
that an actual thread switch did not occur but it would have occurred
in the case of having dedicated threads. The 'total' count is the sum.
* RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(x)
Example:
void MyClass::MyFunction() {
RTC_LOG_THREAD_BLOCK_COUNT();
thread_->Invoke<void>([this](){ DoStuff(); });
thread_->Invoke<void>([this](){ MoreStuff(); });
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
}
When a function is known to have blocking calls and we want to not
regress from the currently known number of blocking calls, we can use
this macro to state that at a certain point in a function, below
where RTC_LOG_THREAD_BLOCK_COUNT() is called, there must have occurred
no more than |x| (total) blocking calls. If more occur, a DCHECK will
hit and print out what the actual number of calls was:
# Fatal error in: my_file.cc, line 5
# last system error: 60
# Check failed: blocked_call_count_printer.GetTotalBlockedCallCount() <= 1 (2 vs. 1)
Bug: webrtc:12649
Change-Id: Ibac4f85f00b89680601dba54a651eac95a0f45d3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213782
Commit-Queue: Tommi <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33632}
2021-04-07 10:08:28 +02:00
|
|
|
EXPECT_EQ(1u, blocked_calls.GetBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(1u, blocked_calls.GetCouldBeBlockingCallCount());
|
|
|
|
|
EXPECT_EQ(2u, blocked_calls.GetTotalBlockedCallCount());
|
|
|
|
|
thread->Stop();
|
|
|
|
|
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2);
|
|
|
|
|
#else
|
|
|
|
|
RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
|
|
|
|
|
RTC_LOG(LS_INFO) << "Test not active in this config";
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-14 12:54:10 +02:00
|
|
|
#if RTC_DCHECK_IS_ON
|
|
|
|
|
TEST(ThreadTest, CountBlockingCallsOneCallback) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread current;
|
2021-04-14 12:54:10 +02:00
|
|
|
bool was_called_back = false;
|
|
|
|
|
{
|
|
|
|
|
rtc::Thread::ScopedCountBlockingCalls blocked_calls(
|
|
|
|
|
[&](uint32_t actual_block, uint32_t could_block) {
|
|
|
|
|
was_called_back = true;
|
|
|
|
|
});
|
2022-09-08 13:13:53 +02:00
|
|
|
current.BlockingCall([]() {});
|
2021-04-14 12:54:10 +02:00
|
|
|
}
|
|
|
|
|
EXPECT_TRUE(was_called_back);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadTest, CountBlockingCallsSkipCallback) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread current;
|
2021-04-14 12:54:10 +02:00
|
|
|
bool was_called_back = false;
|
|
|
|
|
{
|
|
|
|
|
rtc::Thread::ScopedCountBlockingCalls blocked_calls(
|
|
|
|
|
[&](uint32_t actual_block, uint32_t could_block) {
|
|
|
|
|
was_called_back = true;
|
|
|
|
|
});
|
|
|
|
|
// Changed `blocked_calls` to not issue the callback if there are 1 or
|
|
|
|
|
// fewer blocking calls (i.e. we set the minimum required number to 2).
|
|
|
|
|
blocked_calls.set_minimum_call_count_for_callback(2);
|
2022-09-08 13:13:53 +02:00
|
|
|
current.BlockingCall([]() {});
|
2021-04-14 12:54:10 +02:00
|
|
|
}
|
|
|
|
|
// We should not have gotten a call back.
|
|
|
|
|
EXPECT_FALSE(was_called_back);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-05-13 18:00:26 +00:00
|
|
|
// Test that setting thread names doesn't cause a malfunction.
|
|
|
|
|
// There's no easy way to verify the name was set properly at this time.
|
2014-10-09 22:08:15 +00:00
|
|
|
TEST(ThreadTest, Names) {
|
2014-05-13 18:00:26 +00:00
|
|
|
// Default name
|
2017-07-14 14:44:46 -07:00
|
|
|
auto thread = Thread::CreateWithSocketServer();
|
2014-05-13 18:00:26 +00:00
|
|
|
EXPECT_TRUE(thread->Start());
|
|
|
|
|
thread->Stop();
|
|
|
|
|
// Name with no object parameter
|
2017-07-14 14:44:46 -07:00
|
|
|
thread = Thread::CreateWithSocketServer();
|
2017-02-27 14:06:41 -08:00
|
|
|
EXPECT_TRUE(thread->SetName("No object", nullptr));
|
2014-05-13 18:00:26 +00:00
|
|
|
EXPECT_TRUE(thread->Start());
|
|
|
|
|
thread->Stop();
|
|
|
|
|
// Really long name
|
2017-07-14 14:44:46 -07:00
|
|
|
thread = Thread::CreateWithSocketServer();
|
2014-05-13 18:00:26 +00:00
|
|
|
EXPECT_TRUE(thread->SetName("Abcdefghijklmnopqrstuvwxyz1234567890", this));
|
|
|
|
|
EXPECT_TRUE(thread->Start());
|
|
|
|
|
thread->Stop();
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-09 15:41:40 +00:00
|
|
|
TEST(ThreadTest, Wrap) {
|
|
|
|
|
Thread* current_thread = Thread::Current();
|
2019-06-12 11:30:59 +02:00
|
|
|
ThreadManager::Instance()->SetCurrentThread(nullptr);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
CustomThread cthread;
|
|
|
|
|
EXPECT_TRUE(cthread.WrapCurrent());
|
|
|
|
|
EXPECT_EQ(&cthread, Thread::Current());
|
|
|
|
|
EXPECT_TRUE(cthread.RunningForTest());
|
|
|
|
|
EXPECT_FALSE(cthread.IsOwned());
|
|
|
|
|
cthread.UnwrapCurrent();
|
|
|
|
|
EXPECT_FALSE(cthread.RunningForTest());
|
|
|
|
|
}
|
|
|
|
|
ThreadManager::Instance()->SetCurrentThread(current_thread);
|
2014-05-13 18:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-30 13:57:25 +02:00
|
|
|
#if (!defined(NDEBUG) || RTC_DCHECK_IS_ON)
|
2020-07-03 12:09:26 +02:00
|
|
|
TEST(ThreadTest, InvokeToThreadAllowedReturnsTrueWithoutPolicies) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2020-07-03 12:09:26 +02:00
|
|
|
// Create and start the thread.
|
|
|
|
|
auto thread1 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread2 = Thread::CreateWithSocketServer();
|
|
|
|
|
|
2022-07-06 19:42:34 +02:00
|
|
|
thread1->PostTask(
|
|
|
|
|
[&]() { EXPECT_TRUE(thread1->IsInvokeToThreadAllowed(thread2.get())); });
|
2022-05-20 09:12:57 +02:00
|
|
|
main_thread.ProcessMessages(100);
|
2020-07-03 12:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadTest, InvokeAllowedWhenThreadsAdded) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2020-07-03 12:09:26 +02:00
|
|
|
// Create and start the thread.
|
|
|
|
|
auto thread1 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread2 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread3 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread4 = Thread::CreateWithSocketServer();
|
|
|
|
|
|
|
|
|
|
thread1->AllowInvokesToThread(thread2.get());
|
|
|
|
|
thread1->AllowInvokesToThread(thread3.get());
|
|
|
|
|
|
2022-07-06 19:42:34 +02:00
|
|
|
thread1->PostTask([&]() {
|
2020-07-03 12:09:26 +02:00
|
|
|
EXPECT_TRUE(thread1->IsInvokeToThreadAllowed(thread2.get()));
|
|
|
|
|
EXPECT_TRUE(thread1->IsInvokeToThreadAllowed(thread3.get()));
|
|
|
|
|
EXPECT_FALSE(thread1->IsInvokeToThreadAllowed(thread4.get()));
|
2022-07-06 19:42:34 +02:00
|
|
|
});
|
2022-05-20 09:12:57 +02:00
|
|
|
main_thread.ProcessMessages(100);
|
2020-07-03 12:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadTest, InvokesDisallowedWhenDisallowAllInvokes) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2020-07-03 12:09:26 +02:00
|
|
|
// Create and start the thread.
|
|
|
|
|
auto thread1 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread2 = Thread::CreateWithSocketServer();
|
|
|
|
|
|
|
|
|
|
thread1->DisallowAllInvokes();
|
|
|
|
|
|
2022-07-06 19:42:34 +02:00
|
|
|
thread1->PostTask(
|
|
|
|
|
[&]() { EXPECT_FALSE(thread1->IsInvokeToThreadAllowed(thread2.get())); });
|
2022-05-20 09:12:57 +02:00
|
|
|
main_thread.ProcessMessages(100);
|
2020-07-03 12:09:26 +02:00
|
|
|
}
|
2021-07-30 13:57:25 +02:00
|
|
|
#endif // (!defined(NDEBUG) || RTC_DCHECK_IS_ON)
|
2020-07-03 12:09:26 +02:00
|
|
|
|
|
|
|
|
TEST(ThreadTest, InvokesAllowedByDefault) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2020-07-03 12:09:26 +02:00
|
|
|
// Create and start the thread.
|
|
|
|
|
auto thread1 = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread2 = Thread::CreateWithSocketServer();
|
|
|
|
|
|
2022-07-06 19:42:34 +02:00
|
|
|
thread1->PostTask(
|
|
|
|
|
[&]() { EXPECT_TRUE(thread1->IsInvokeToThreadAllowed(thread2.get())); });
|
2022-05-20 09:12:57 +02:00
|
|
|
main_thread.ProcessMessages(100);
|
2020-07-03 12:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-08 13:13:53 +02:00
|
|
|
TEST(ThreadTest, BlockingCall) {
|
2014-05-13 18:00:26 +00:00
|
|
|
// Create and start the thread.
|
2017-07-14 14:44:46 -07:00
|
|
|
auto thread = Thread::CreateWithSocketServer();
|
|
|
|
|
thread->Start();
|
2014-05-13 18:00:26 +00:00
|
|
|
// Try calling functors.
|
2022-09-08 13:13:53 +02:00
|
|
|
EXPECT_EQ(42, thread->BlockingCall([] { return 42; }));
|
|
|
|
|
bool called = false;
|
|
|
|
|
thread->BlockingCall([&] { called = true; });
|
|
|
|
|
EXPECT_TRUE(called);
|
|
|
|
|
|
2014-05-13 18:00:26 +00:00
|
|
|
// Try calling bare functions.
|
|
|
|
|
struct LocalFuncs {
|
|
|
|
|
static int Func1() { return 999; }
|
|
|
|
|
static void Func2() {}
|
|
|
|
|
};
|
2022-09-08 13:13:53 +02:00
|
|
|
EXPECT_EQ(999, thread->BlockingCall(&LocalFuncs::Func1));
|
|
|
|
|
thread->BlockingCall(&LocalFuncs::Func2);
|
2014-05-13 18:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:14:05 +00:00
|
|
|
// Verifies that two threads calling Invoke on each other at the same time does
|
2020-03-03 10:48:05 +01:00
|
|
|
// not deadlock but crash.
|
|
|
|
|
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
|
|
|
|
TEST(ThreadTest, TwoThreadsInvokeDeathTest) {
|
2021-07-28 08:55:52 +02:00
|
|
|
GTEST_FLAG_SET(death_test_style, "threadsafe");
|
2014-09-24 17:14:05 +00:00
|
|
|
AutoThread thread;
|
2020-03-03 10:48:05 +01:00
|
|
|
Thread* main_thread = Thread::Current();
|
2017-07-14 14:44:46 -07:00
|
|
|
auto other_thread = Thread::CreateWithSocketServer();
|
|
|
|
|
other_thread->Start();
|
2022-09-08 13:13:53 +02:00
|
|
|
other_thread->BlockingCall([main_thread] {
|
|
|
|
|
RTC_EXPECT_DEATH(main_thread->BlockingCall([] {}), "loop");
|
2020-03-03 10:48:05 +01:00
|
|
|
});
|
|
|
|
|
}
|
2014-09-24 17:14:05 +00:00
|
|
|
|
2020-03-03 10:48:05 +01:00
|
|
|
TEST(ThreadTest, ThreeThreadsInvokeDeathTest) {
|
2021-07-28 08:55:52 +02:00
|
|
|
GTEST_FLAG_SET(death_test_style, "threadsafe");
|
2020-03-03 10:48:05 +01:00
|
|
|
AutoThread thread;
|
|
|
|
|
Thread* first = Thread::Current();
|
|
|
|
|
|
|
|
|
|
auto second = Thread::Create();
|
|
|
|
|
second->Start();
|
|
|
|
|
auto third = Thread::Create();
|
|
|
|
|
third->Start();
|
|
|
|
|
|
2022-09-08 13:13:53 +02:00
|
|
|
second->BlockingCall([&] {
|
|
|
|
|
third->BlockingCall(
|
|
|
|
|
[&] { RTC_EXPECT_DEATH(first->BlockingCall([] {}), "loop"); });
|
2020-03-03 10:48:05 +01:00
|
|
|
});
|
2014-09-24 17:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-03 10:48:05 +01:00
|
|
|
#endif
|
|
|
|
|
|
2014-09-24 17:14:05 +00:00
|
|
|
// Verifies that if thread A invokes a call on thread B and thread C is trying
|
|
|
|
|
// to invoke A at the same time, thread A does not handle C's invoke while
|
|
|
|
|
// invoking B.
|
2022-09-08 13:13:53 +02:00
|
|
|
TEST(ThreadTest, ThreeThreadsBlockingCall) {
|
2014-09-24 17:14:05 +00:00
|
|
|
AutoThread thread;
|
|
|
|
|
Thread* thread_a = Thread::Current();
|
2017-07-14 14:44:46 -07:00
|
|
|
auto thread_b = Thread::CreateWithSocketServer();
|
|
|
|
|
auto thread_c = Thread::CreateWithSocketServer();
|
|
|
|
|
thread_b->Start();
|
|
|
|
|
thread_c->Start();
|
2014-09-24 17:14:05 +00:00
|
|
|
|
2014-10-15 14:54:56 +00:00
|
|
|
class LockedBool {
|
|
|
|
|
public:
|
|
|
|
|
explicit LockedBool(bool value) : value_(value) {}
|
|
|
|
|
|
|
|
|
|
void Set(bool value) {
|
2020-07-10 13:23:25 +02:00
|
|
|
webrtc::MutexLock lock(&mutex_);
|
2014-10-15 14:54:56 +00:00
|
|
|
value_ = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Get() {
|
2020-07-10 13:23:25 +02:00
|
|
|
webrtc::MutexLock lock(&mutex_);
|
2014-10-15 14:54:56 +00:00
|
|
|
return value_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2020-07-10 13:23:25 +02:00
|
|
|
webrtc::Mutex mutex_;
|
|
|
|
|
bool value_ RTC_GUARDED_BY(mutex_);
|
2014-10-15 14:54:56 +00:00
|
|
|
};
|
|
|
|
|
|
2014-09-24 17:14:05 +00:00
|
|
|
struct LocalFuncs {
|
2014-10-15 14:54:56 +00:00
|
|
|
static void Set(LockedBool* out) { out->Set(true); }
|
|
|
|
|
static void InvokeSet(Thread* thread, LockedBool* out) {
|
2022-09-08 13:13:53 +02:00
|
|
|
thread->BlockingCall([out] { Set(out); });
|
2014-09-24 17:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-26 16:03:14 +02:00
|
|
|
// Set `out` true and call InvokeSet on `thread`.
|
2014-10-15 14:54:56 +00:00
|
|
|
static void SetAndInvokeSet(LockedBool* out,
|
|
|
|
|
Thread* thread,
|
|
|
|
|
LockedBool* out_inner) {
|
|
|
|
|
out->Set(true);
|
2014-09-24 17:14:05 +00:00
|
|
|
InvokeSet(thread, out_inner);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-26 16:03:14 +02:00
|
|
|
// Asynchronously invoke SetAndInvokeSet on `thread1` and wait until
|
|
|
|
|
// `thread1` starts the call.
|
2022-09-02 11:10:24 +02:00
|
|
|
static void AsyncInvokeSetAndWait(Thread* thread1,
|
2017-02-23 17:10:07 -08:00
|
|
|
Thread* thread2,
|
|
|
|
|
LockedBool* out) {
|
2014-10-15 14:54:56 +00:00
|
|
|
LockedBool async_invoked(false);
|
2014-09-24 17:14:05 +00:00
|
|
|
|
2022-09-02 11:10:24 +02:00
|
|
|
thread1->PostTask([&async_invoked, thread2, out] {
|
|
|
|
|
SetAndInvokeSet(&async_invoked, thread2, out);
|
|
|
|
|
});
|
2014-09-24 17:14:05 +00:00
|
|
|
|
2025-01-17 13:19:45 +00:00
|
|
|
EXPECT_THAT(webrtc::WaitUntil([&] { return async_invoked.Get(); },
|
|
|
|
|
::testing::IsTrue()),
|
|
|
|
|
webrtc::IsRtcOk());
|
2014-09-24 17:14:05 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-10-15 14:54:56 +00:00
|
|
|
LockedBool thread_a_called(false);
|
2014-09-24 17:14:05 +00:00
|
|
|
|
|
|
|
|
// Start the sequence A --(invoke)--> B --(async invoke)--> C --(invoke)--> A.
|
|
|
|
|
// Thread B returns when C receives the call and C should be blocked until A
|
|
|
|
|
// starts to process messages.
|
2021-01-18 11:35:23 +01:00
|
|
|
Thread* thread_c_ptr = thread_c.get();
|
2022-09-08 13:13:53 +02:00
|
|
|
thread_b->BlockingCall([thread_c_ptr, thread_a, &thread_a_called] {
|
2022-09-02 11:10:24 +02:00
|
|
|
LocalFuncs::AsyncInvokeSetAndWait(thread_c_ptr, thread_a, &thread_a_called);
|
|
|
|
|
});
|
2014-10-15 14:54:56 +00:00
|
|
|
EXPECT_FALSE(thread_a_called.Get());
|
2014-09-24 17:14:05 +00:00
|
|
|
|
2025-01-17 13:19:45 +00:00
|
|
|
EXPECT_THAT(webrtc::WaitUntil([&] { return thread_a_called.Get(); },
|
|
|
|
|
::testing::IsTrue()),
|
|
|
|
|
webrtc::IsRtcOk());
|
2014-09-24 17:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-24 12:19:46 +02:00
|
|
|
static void DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(
|
|
|
|
|
FakeClock& clock,
|
|
|
|
|
Thread& q) {
|
|
|
|
|
std::vector<int> run_order;
|
2020-01-16 11:15:35 +01:00
|
|
|
|
2022-08-24 12:19:46 +02:00
|
|
|
Event done;
|
|
|
|
|
int64_t now = TimeMillis();
|
|
|
|
|
q.PostDelayedTask([&] { run_order.push_back(3); }, TimeDelta::Millis(3));
|
|
|
|
|
q.PostDelayedTask([&] { run_order.push_back(0); }, TimeDelta::Millis(1));
|
|
|
|
|
q.PostDelayedTask([&] { run_order.push_back(1); }, TimeDelta::Millis(2));
|
|
|
|
|
q.PostDelayedTask([&] { run_order.push_back(4); }, TimeDelta::Millis(3));
|
|
|
|
|
q.PostDelayedTask([&] { run_order.push_back(2); }, TimeDelta::Millis(2));
|
|
|
|
|
q.PostDelayedTask([&] { done.Set(); }, TimeDelta::Millis(4));
|
|
|
|
|
// Validate time was frozen while tasks were posted.
|
|
|
|
|
RTC_DCHECK_EQ(TimeMillis(), now);
|
|
|
|
|
|
|
|
|
|
// Change time to make all tasks ready to run and wait for them.
|
|
|
|
|
clock.AdvanceTime(TimeDelta::Millis(4));
|
|
|
|
|
ASSERT_TRUE(done.Wait(TimeDelta::Seconds(1)));
|
|
|
|
|
|
|
|
|
|
EXPECT_THAT(run_order, ElementsAre(0, 1, 2, 3, 4));
|
2020-01-16 11:15:35 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-26 11:49:14 +02:00
|
|
|
TEST(ThreadTest, DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder) {
|
2022-08-24 12:19:46 +02:00
|
|
|
ScopedBaseFakeClock clock;
|
2021-01-15 10:41:01 +01:00
|
|
|
Thread q(CreateDefaultSocketServer(), true);
|
2022-08-24 12:19:46 +02:00
|
|
|
q.Start();
|
|
|
|
|
DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(clock, q);
|
2020-01-16 11:15:35 +01:00
|
|
|
|
|
|
|
|
NullSocketServer nullss;
|
|
|
|
|
Thread q_nullss(&nullss, true);
|
2022-08-24 12:19:46 +02:00
|
|
|
q_nullss.Start();
|
|
|
|
|
DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(clock, q_nullss);
|
2020-01-16 11:15:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that ProcessAllMessageQueues does its essential function; process
|
|
|
|
|
// all messages (both delayed and non delayed) up until the current time, on
|
|
|
|
|
// all registered message queues.
|
|
|
|
|
TEST(ThreadManager, ProcessAllMessageQueues) {
|
2022-05-20 09:12:57 +02:00
|
|
|
rtc::AutoThread main_thread;
|
2020-01-16 11:15:35 +01:00
|
|
|
Event entered_process_all_message_queues(true, false);
|
|
|
|
|
auto a = Thread::CreateWithSocketServer();
|
|
|
|
|
auto b = Thread::CreateWithSocketServer();
|
|
|
|
|
a->Start();
|
|
|
|
|
b->Start();
|
|
|
|
|
|
2022-06-27 09:47:02 +02:00
|
|
|
std::atomic<int> messages_processed(0);
|
2020-01-16 11:15:35 +01:00
|
|
|
auto incrementer = [&messages_processed,
|
|
|
|
|
&entered_process_all_message_queues] {
|
|
|
|
|
// Wait for event as a means to ensure Increment doesn't occur outside
|
|
|
|
|
// of ProcessAllMessageQueues. The event is set by a message posted to
|
|
|
|
|
// the main thread, which is guaranteed to be handled inside
|
|
|
|
|
// ProcessAllMessageQueues.
|
|
|
|
|
entered_process_all_message_queues.Wait(Event::kForever);
|
2022-06-27 09:47:02 +02:00
|
|
|
messages_processed.fetch_add(1);
|
2020-01-16 11:15:35 +01:00
|
|
|
};
|
|
|
|
|
auto event_signaler = [&entered_process_all_message_queues] {
|
|
|
|
|
entered_process_all_message_queues.Set();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Post messages (both delayed and non delayed) to both threads.
|
2022-07-06 19:42:34 +02:00
|
|
|
a->PostTask(incrementer);
|
|
|
|
|
b->PostTask(incrementer);
|
|
|
|
|
a->PostDelayedTask(incrementer, TimeDelta::Zero());
|
|
|
|
|
b->PostDelayedTask(incrementer, TimeDelta::Zero());
|
|
|
|
|
main_thread.PostTask(event_signaler);
|
2020-01-16 11:15:35 +01:00
|
|
|
|
|
|
|
|
ThreadManager::ProcessAllMessageQueuesForTesting();
|
2022-06-27 09:47:02 +02:00
|
|
|
EXPECT_EQ(4, messages_processed.load(std::memory_order_acquire));
|
2020-01-16 11:15:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test that ProcessAllMessageQueues doesn't hang if a thread is quitting.
|
|
|
|
|
TEST(ThreadManager, ProcessAllMessageQueuesWithQuittingThread) {
|
|
|
|
|
auto t = Thread::CreateWithSocketServer();
|
|
|
|
|
t->Start();
|
|
|
|
|
t->Quit();
|
|
|
|
|
ThreadManager::ProcessAllMessageQueuesForTesting();
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 09:34:06 +01:00
|
|
|
void WaitAndSetEvent(Event* wait_event, Event* set_event) {
|
|
|
|
|
wait_event->Wait(Event::kForever);
|
|
|
|
|
set_event->Set();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A functor that keeps track of the number of copies and moves.
|
|
|
|
|
class LifeCycleFunctor {
|
|
|
|
|
public:
|
|
|
|
|
struct Stats {
|
|
|
|
|
size_t copy_count = 0;
|
|
|
|
|
size_t move_count = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor(Stats* stats, Event* event) : stats_(stats), event_(event) {}
|
|
|
|
|
LifeCycleFunctor(const LifeCycleFunctor& other) { *this = other; }
|
|
|
|
|
LifeCycleFunctor(LifeCycleFunctor&& other) { *this = std::move(other); }
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor& operator=(const LifeCycleFunctor& other) {
|
|
|
|
|
stats_ = other.stats_;
|
|
|
|
|
event_ = other.event_;
|
|
|
|
|
++stats_->copy_count;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor& operator=(LifeCycleFunctor&& other) {
|
|
|
|
|
stats_ = other.stats_;
|
|
|
|
|
event_ = other.event_;
|
|
|
|
|
++stats_->move_count;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operator()() { event_->Set(); }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Stats* stats_;
|
|
|
|
|
Event* event_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A functor that verifies the thread it was destroyed on.
|
|
|
|
|
class DestructionFunctor {
|
|
|
|
|
public:
|
|
|
|
|
DestructionFunctor(Thread* thread, bool* thread_was_current, Event* event)
|
|
|
|
|
: thread_(thread),
|
|
|
|
|
thread_was_current_(thread_was_current),
|
|
|
|
|
event_(event) {}
|
|
|
|
|
~DestructionFunctor() {
|
|
|
|
|
// Only signal the event if this was the functor that was invoked to avoid
|
|
|
|
|
// the event being signaled due to the destruction of temporary/moved
|
|
|
|
|
// versions of this object.
|
|
|
|
|
if (was_invoked_) {
|
|
|
|
|
*thread_was_current_ = thread_->IsCurrent();
|
|
|
|
|
event_->Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void operator()() { was_invoked_ = true; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Thread* thread_;
|
|
|
|
|
bool* thread_was_current_;
|
|
|
|
|
Event* event_;
|
|
|
|
|
bool was_invoked_ = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithLambda) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event event;
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask([&event] { event.Set(); });
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithCopiedFunctor) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor::Stats stats;
|
|
|
|
|
Event event;
|
|
|
|
|
LifeCycleFunctor functor(&stats, &event);
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(functor);
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(1u, stats.copy_count);
|
|
|
|
|
EXPECT_EQ(0u, stats.move_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithMovedFunctor) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor::Stats stats;
|
|
|
|
|
Event event;
|
|
|
|
|
LifeCycleFunctor functor(&stats, &event);
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(std::move(functor));
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(0u, stats.copy_count);
|
|
|
|
|
EXPECT_EQ(1u, stats.move_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithReferencedFunctorShouldCopy) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
LifeCycleFunctor::Stats stats;
|
|
|
|
|
Event event;
|
|
|
|
|
LifeCycleFunctor functor(&stats, &event);
|
|
|
|
|
LifeCycleFunctor& functor_ref = functor;
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(functor_ref);
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(1u, stats.copy_count);
|
|
|
|
|
EXPECT_EQ(0u, stats.move_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithCopiedFunctorDestroyedOnTargetThread) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event event;
|
|
|
|
|
bool was_invoked_on_background_thread = false;
|
|
|
|
|
DestructionFunctor functor(background_thread.get(),
|
|
|
|
|
&was_invoked_on_background_thread, &event);
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(functor);
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesWithMovedFunctorDestroyedOnTargetThread) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event event;
|
|
|
|
|
bool was_invoked_on_background_thread = false;
|
|
|
|
|
DestructionFunctor functor(background_thread.get(),
|
|
|
|
|
&was_invoked_on_background_thread, &event);
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(std::move(functor));
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest,
|
|
|
|
|
InvokesWithReferencedFunctorShouldCopyAndDestroyedOnTargetThread) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event event;
|
|
|
|
|
bool was_invoked_on_background_thread = false;
|
|
|
|
|
DestructionFunctor functor(background_thread.get(),
|
|
|
|
|
&was_invoked_on_background_thread, &event);
|
|
|
|
|
DestructionFunctor& functor_ref = functor;
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(functor_ref);
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesOnBackgroundThread) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event event;
|
|
|
|
|
bool was_invoked_on_background_thread = false;
|
2021-01-18 11:35:23 +01:00
|
|
|
Thread* background_thread_ptr = background_thread.get();
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(
|
2021-01-18 11:35:23 +01:00
|
|
|
[background_thread_ptr, &was_invoked_on_background_thread, &event] {
|
|
|
|
|
was_invoked_on_background_thread = background_thread_ptr->IsCurrent();
|
|
|
|
|
event.Set();
|
|
|
|
|
});
|
2019-02-28 09:34:06 +01:00
|
|
|
event.Wait(Event::kForever);
|
|
|
|
|
|
|
|
|
|
EXPECT_TRUE(was_invoked_on_background_thread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesAsynchronously) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
// The first event ensures that SendSingleMessage() is not blocking this
|
|
|
|
|
// thread. The second event ensures that the message is processed.
|
|
|
|
|
Event event_set_by_test_thread;
|
|
|
|
|
Event event_set_by_background_thread;
|
2023-04-19 17:35:28 -07:00
|
|
|
background_thread->PostTask([&event_set_by_test_thread,
|
|
|
|
|
&event_set_by_background_thread] {
|
|
|
|
|
WaitAndSetEvent(&event_set_by_test_thread, &event_set_by_background_thread);
|
|
|
|
|
});
|
2019-02-28 09:34:06 +01:00
|
|
|
event_set_by_test_thread.Set();
|
|
|
|
|
event_set_by_background_thread.Wait(Event::kForever);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostTaskTest, InvokesInPostedOrder) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event first;
|
|
|
|
|
Event second;
|
|
|
|
|
Event third;
|
|
|
|
|
Event fourth;
|
|
|
|
|
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostTask(
|
|
|
|
|
[&first, &second] { WaitAndSetEvent(&first, &second); });
|
|
|
|
|
background_thread->PostTask(
|
|
|
|
|
[&second, &third] { WaitAndSetEvent(&second, &third); });
|
|
|
|
|
background_thread->PostTask(
|
|
|
|
|
[&third, &fourth] { WaitAndSetEvent(&third, &fourth); });
|
2019-02-28 09:34:06 +01:00
|
|
|
|
|
|
|
|
// All tasks have been posted before the first one is unblocked.
|
|
|
|
|
first.Set();
|
|
|
|
|
// Only if the chain is invoked in posted order will the last event be set.
|
|
|
|
|
fourth.Wait(Event::kForever);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 11:21:53 -08:00
|
|
|
TEST(ThreadPostDelayedTaskTest, InvokesAsynchronously) {
|
|
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
// The first event ensures that SendSingleMessage() is not blocking this
|
|
|
|
|
// thread. The second event ensures that the message is processed.
|
|
|
|
|
Event event_set_by_test_thread;
|
|
|
|
|
Event event_set_by_background_thread;
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostDelayedTask(
|
2021-01-18 11:35:23 +01:00
|
|
|
[&event_set_by_test_thread, &event_set_by_background_thread] {
|
|
|
|
|
WaitAndSetEvent(&event_set_by_test_thread,
|
|
|
|
|
&event_set_by_background_thread);
|
|
|
|
|
},
|
2022-07-06 19:42:34 +02:00
|
|
|
TimeDelta::Millis(10));
|
2019-12-11 11:21:53 -08:00
|
|
|
event_set_by_test_thread.Set();
|
|
|
|
|
event_set_by_background_thread.Wait(Event::kForever);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ThreadPostDelayedTaskTest, InvokesInDelayOrder) {
|
2019-12-16 00:56:02 -08:00
|
|
|
ScopedFakeClock clock;
|
2019-12-11 11:21:53 -08:00
|
|
|
std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
|
|
|
|
|
background_thread->Start();
|
|
|
|
|
|
|
|
|
|
Event first;
|
|
|
|
|
Event second;
|
|
|
|
|
Event third;
|
|
|
|
|
Event fourth;
|
|
|
|
|
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostDelayedTask(
|
|
|
|
|
[&third, &fourth] { WaitAndSetEvent(&third, &fourth); },
|
2022-07-06 19:42:34 +02:00
|
|
|
TimeDelta::Millis(11));
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostDelayedTask(
|
|
|
|
|
[&first, &second] { WaitAndSetEvent(&first, &second); },
|
2022-07-06 19:42:34 +02:00
|
|
|
TimeDelta::Millis(9));
|
2022-01-24 09:57:03 +01:00
|
|
|
background_thread->PostDelayedTask(
|
|
|
|
|
[&second, &third] { WaitAndSetEvent(&second, &third); },
|
2022-07-06 19:42:34 +02:00
|
|
|
TimeDelta::Millis(10));
|
2019-12-11 11:21:53 -08:00
|
|
|
|
|
|
|
|
// All tasks have been posted before the first one is unblocked.
|
|
|
|
|
first.Set();
|
2019-12-16 00:56:02 -08:00
|
|
|
// Only if the chain is invoked in delay order will the last event be set.
|
2022-07-06 19:42:34 +02:00
|
|
|
clock.AdvanceTime(TimeDelta::Millis(11));
|
2022-08-19 08:16:48 +00:00
|
|
|
EXPECT_TRUE(fourth.Wait(TimeDelta::Zero()));
|
2019-12-11 11:21:53 -08:00
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:11:56 +02:00
|
|
|
TEST(ThreadPostDelayedTaskTest, IsCurrentTaskQueue) {
|
|
|
|
|
auto current_tq = webrtc::TaskQueueBase::Current();
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<rtc::Thread> thread(rtc::Thread::Create());
|
|
|
|
|
thread->WrapCurrent();
|
|
|
|
|
EXPECT_EQ(webrtc::TaskQueueBase::Current(),
|
|
|
|
|
static_cast<webrtc::TaskQueueBase*>(thread.get()));
|
|
|
|
|
thread->UnwrapCurrent();
|
|
|
|
|
}
|
|
|
|
|
EXPECT_EQ(webrtc::TaskQueueBase::Current(), current_tq);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-22 15:52:40 +01:00
|
|
|
class ThreadFactory : public webrtc::TaskQueueFactory {
|
|
|
|
|
public:
|
|
|
|
|
std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
|
|
|
|
|
CreateTaskQueue(absl::string_view /* name */,
|
|
|
|
|
Priority /*priority*/) const override {
|
|
|
|
|
std::unique_ptr<Thread> thread = Thread::Create();
|
|
|
|
|
thread->Start();
|
|
|
|
|
return std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>(
|
|
|
|
|
thread.release());
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-23 12:57:16 +02:00
|
|
|
std::unique_ptr<webrtc::TaskQueueFactory> CreateDefaultThreadFactory(
|
|
|
|
|
const webrtc::FieldTrialsView*) {
|
|
|
|
|
return std::make_unique<ThreadFactory>();
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-22 15:52:40 +01:00
|
|
|
using ::webrtc::TaskQueueTest;
|
|
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(RtcThread,
|
|
|
|
|
TaskQueueTest,
|
2022-08-23 12:57:16 +02:00
|
|
|
::testing::Values(CreateDefaultThreadFactory));
|
2019-11-22 15:52:40 +01:00
|
|
|
|
2018-12-11 18:43:40 +01:00
|
|
|
} // namespace
|
|
|
|
|
} // namespace rtc
|