/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <mutex> #include <future> #include <chrono> #include <random> #include <gtest/gtest.h> #include "sync/sync.h" #include "ffrt_inner.h" #include "dfx/log/ffrt_log_api.h" #include "c/thread.h" #include "../common.h" extern "C" int ffrt_mutexattr_init(ffrt_mutexattr_t* attr); extern "C" int ffrt_mutexattr_settype(ffrt_mutexattr_t* attr, int type); extern "C" int ffrt_mutexattr_gettype(ffrt_mutexattr_t* attr, int* type); extern "C" int ffrt_mutexattr_destroy(ffrt_mutexattr_t* attr); extern "C" int ffrt_mutex_init(ffrt_mutex_t *mutex, const ffrt_mutexattr_t* attr); extern "C" int ffrt_mutex_lock(ffrt_mutex_t *mutex); extern "C" int ffrt_mutex_unlock(ffrt_mutex_t *mutex); extern "C" int ffrt_mutex_trylock(ffrt_mutex_t *mutex); extern "C" int ffrt_mutex_destroy(ffrt_mutex_t *mutex); using namespace std; using namespace testing; #ifdef HWTEST_TESTING_EXT_ENABLE using namespace testing::ext; #endif class SyncTest : public testing::Test { protected: static void SetUpTestCase() { } static void TearDownTestCase() { } virtual void SetUp() { } virtual void TearDown() { } }; HWTEST_F(SyncTest, mutexattr_nullptr_fail, TestSize.Level1) { int ret = ffrt_mutexattr_init(nullptr); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutexattr_settype(nullptr, 0); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutexattr_gettype(nullptr, nullptr); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutexattr_destroy(nullptr); EXPECT_EQ(ret, ffrt_error_inval); } HWTEST_F(SyncTest, mutex_nullptr_fail, TestSize.Level1) { int ret = ffrt_mutex_init(nullptr, nullptr); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutex_lock(nullptr); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutex_unlock(nullptr); EXPECT_EQ(ret, ffrt_error_inval); ret = ffrt_mutex_trylock(nullptr); EXPECT_EQ(ret, ffrt_error_inval); ffrt_mutex_destroy(nullptr); } HWTEST_F(SyncTest, recursive_mutex_try_lock, TestSize.Level1) { int val = -1; ffrt::recursive_mutex lock; lock.lock(); val = lock.try_lock(); EXPECT_EQ(val, 1); lock.unlock(); val = lock.try_lock(); EXPECT_EQ(val, 1); lock.unlock(); lock.unlock(); } HWTEST_F(SyncTest, class_data_align, TestSize.Level1) { struct memTest { bool isFlag; // Construct an unaligned address ffrt::mutex mtx; ffrt::task_attr taskAttr; ffrt::task_handle taskHandle; ffrt::condition_variable cv; ffrt::thread t; }; memTest m; { ffrt::mutex* mtxAddr = &m.mtx; uintptr_t addr_int = reinterpret_cast<uintptr_t>(mtxAddr); EXPECT_EQ((addr_int % 4), 0); ffrt::task_attr* attrAddr = &m.taskAttr; addr_int = reinterpret_cast<uintptr_t>(attrAddr); EXPECT_EQ((addr_int % 4), 0); ffrt::task_handle* handleAddr = &m.taskHandle; addr_int = reinterpret_cast<uintptr_t>(handleAddr); EXPECT_EQ((addr_int % 4), 0); ffrt::condition_variable* cvAddr = &m.cv; addr_int = reinterpret_cast<uintptr_t>(cvAddr); EXPECT_EQ((addr_int % 4), 0); ffrt::thread* tAddr = &m.t; addr_int = reinterpret_cast<uintptr_t>(tAddr); EXPECT_EQ((addr_int % 4), 0); } } HWTEST_F(SyncTest, lock_stress, TestSize.Level1) { // trigger lazy init ffrt::submit([&]() {}, {}, {}); ffrt::wait(); const int N = 10; const int M = 1000; const int J = 10000; ffrt::mutex lock; // std::mutex lock; int acc = 0; for (int i = 0; i < N; ++i) { ffrt::submit( [&]() { for (int j = 0; j < M; ++j) { lock.lock(); acc++; lock.unlock(); } }, {}, {}); } for (int j = 0; j < J; ++j) { lock.lock(); acc++; lock.unlock(); } ffrt::wait(); EXPECT_EQ(acc, (M * N + J)); } HWTEST_F(SyncTest, lock_stress_c_api, TestSize.Level1) { // trigger lazy init ffrt::submit([&]() {}, {}, {}); ffrt::wait(); const int N = 10; const int M = 1000; const int J = 10000; ffrt::mutex* lock = new ffrt::mutex; int acc = 0; for (int i = 0; i < N; ++i) { ffrt::submit( [&]() { for (int j = 0; j < M; ++j) { lock->lock(); acc++; lock->unlock(); } }, {}, {}); } for (int j = 0; j < J; ++j) { lock->lock(); acc++; lock->unlock(); } ffrt::wait(); EXPECT_EQ(acc, (M * N + J)); delete lock; } HWTEST_F(SyncTest, conditionTestNotifyOne, TestSize.Level1) { ffrt::condition_variable cond; int a = 0; ffrt::mutex lock_; ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait(lck, [&] { return a == 1; }); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); a = 1; cond.notify_one(); }, {}, {}); ffrt::wait(); } HWTEST_F(SyncTest, conditionTestNotifyAll, TestSize.Level1) { ffrt::condition_variable cond; int a = 0; ffrt::mutex lock_; ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait(lck, [&] { return a == 1; }); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait(lck, [&] { return a == 1; }); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait(lck, [&] { return a == 1; }); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); a = 1; cond.notify_all(); }, {}, {}); ffrt::wait(); } HWTEST_F(SyncTest, conditionTestWaitfor, TestSize.Level1) { ffrt::condition_variable cond; std::atomic_int a = 0; ffrt::mutex lock_; ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait_for(lck, 100ms, [&] { return a == 1; }); EXPECT_EQ(a, 2); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); cond.wait_for(lck, 150ms, [&] { return a == 2; }); EXPECT_EQ(a, 2); }, {}, {}); ffrt::submit( [&]() { std::unique_lock lck(lock_); a = 2; cond.notify_all(); }, {}, {}); ffrt::wait(); } HWTEST_F(SyncTest, conditionTestDataRace, TestSize.Level1) { std::atomic_bool exit {false}; ffrt::mutex mtx; ffrt::condition_variable cv; std::atomic_bool start {false}; ffrt::thread th {[&] { while (!exit) { if (start) { cv.notify_one(); ffrt::this_task::sleep_for(1us); } } }}; start = true; for (int i = 0; i < 2000; ++i) { std::unique_lock lk(mtx); cv.wait_for(lk, 1us); } exit = true; th.join(); exit = false; start = true; ffrt::thread th1 {[&] { for (int i = 0; i < 2000; ++i) { std::unique_lock lk(mtx); cv.wait_for(lk, 1us); } exit = true; }}; while (!exit) { if (start) { cv.notify_one(); ffrt::this_task::sleep_for(1us); } } th1.join(); } static void NotifyOneTest(ffrt::mutex& mtx, ffrt::condition_variable& cv) { FFRT_LOGE("[RUN ] notifyone"); int value = 0; bool flag {false}; ffrt::submit( [&]() { std::unique_lock lk(mtx); cv.wait(lk, [&] { return flag; }); EXPECT_TRUE(lk.owns_lock()); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { { std::unique_lock lk(mtx); flag = true; } cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitUntilTimeoutTest(ffrt::mutex& mtx, ffrt::condition_variable& cv) { constexpr auto eps = 3ms; FFRT_LOGE("[RUN ] WaitUntil timeout¬ifyone"); int value = 0; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_EQ(static_cast<int>(cv.wait_until(lk, std::chrono::steady_clock::now() + 30ms)), static_cast<int>(ffrt::cv_status::timeout)); EXPECT_TRUE(lk.owns_lock()); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { ffrt::this_task::sleep_for(30ms + eps); cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitUtilFlagTest_1(ffrt::mutex& mtx, ffrt::condition_variable& cv) { constexpr auto eps = 3ms; FFRT_LOGE("[RUN ] WaitUntil flag¬ifyone"); int value = 0; bool flag {false}; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_TRUE(!cv.wait_until(lk, std::chrono::steady_clock::now() + 30ms, [&] { return flag; })); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { ffrt::this_task::sleep_for(30ms + eps); cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitUtilFlagTest_2(ffrt::mutex& mtx, ffrt::condition_variable& cv) { int value = 0; bool flag {false}; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_TRUE(cv.wait_until(lk, std::chrono::steady_clock::now() + 30ms, [&] { return flag; })); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { std::unique_lock lk(mtx); flag = true; cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitForTest_1(ffrt::mutex& mtx, ffrt::condition_variable& cv) { constexpr auto eps = 3ms; int value = 0; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_EQ(static_cast<int>(cv.wait_for(lk, 30ms)), static_cast<int>(ffrt::cv_status::timeout)); EXPECT_TRUE(lk.owns_lock()); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { ffrt::this_task::sleep_for(30ms + eps); cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitForTest_2(ffrt::mutex& mtx, ffrt::condition_variable& cv) { int value = 0; ffrt::submit( [&]() { std::unique_lock lk(mtx); ffrt::submit( [&]() { std::unique_lock lk(mtx); cv.notify_one(); }, {}, {}); EXPECT_EQ(static_cast<int>(cv.wait_for(lk, 30ms)), static_cast<int>(ffrt::cv_status::no_timeout)); EXPECT_EQ(value, 0); EXPECT_TRUE(lk.owns_lock()); value = 123; }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitForTest_3(ffrt::mutex& mtx, ffrt::condition_variable& cv) { constexpr auto eps = 3ms; int value = 0; bool flag {false}; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_TRUE(!cv.wait_for(lk, 30ms, [&] { return flag; })); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { ffrt::this_task::sleep_for(30ms + eps); cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } static void WaitForTest_4(ffrt::mutex& mtx, ffrt::condition_variable& cv) { int value = 0; bool flag {false}; ffrt::submit( [&]() { std::unique_lock lk(mtx); EXPECT_TRUE(cv.wait_for(lk, 30ms, [&] { return flag; })); value = 123; }, {}, {}); EXPECT_EQ(value, 0); ffrt::submit( [&]() { std::unique_lock lk(mtx); flag = true; cv.notify_one(); }, {}, {}); ffrt::wait(); EXPECT_EQ(value, 123); } HWTEST_F(SyncTest, conditionTest, TestSize.Level1) { ffrt::mutex mtx; ffrt::condition_variable cv; NotifyOneTest(mtx, cv); WaitUntilTimeoutTest(mtx, cv); WaitUtilFlagTest_1(mtx, cv); WaitUtilFlagTest_2(mtx, cv); WaitForTest_1(mtx, cv); WaitForTest_2(mtx, cv); WaitForTest_3(mtx, cv); WaitForTest_4(mtx, cv); } static void LockTest(ffrt::shared_mutex& smtx) { int x = 0; const int N = 100; const int R = 200; ffrt::submit( [&]() { for (int i = 0; i < N; i++) { smtx.lock(); x++; smtx.unlock(); } }, {}, {}); for (int j = 0; j < N; ++j) { smtx.lock(); x++; smtx.unlock(); } ffrt::wait(); EXPECT_EQ(x, R); } static void TryLockTest(ffrt::shared_mutex& smtx) { int x = 0; const int N = 100; ffrt::submit( [&]() { smtx.lock(); ffrt::this_task::sleep_for(20ms); smtx.unlock(); }, {}, {}); ffrt::this_task::sleep_for(2ms); bool ret = smtx.try_lock(); EXPECT_EQ(ret, false); if (ret) { smtx.unlock(); } ffrt::wait(); ret = smtx.try_lock(); EXPECT_EQ(ret, true); if (ret) { smtx.unlock(); } } static void LockSharedTest(ffrt::shared_mutex& smtx) { int x = 0; const int N = 100; ffrt::submit( [&]() { smtx.lock_shared(); ffrt::this_task::sleep_for(20ms); x = N; smtx.unlock_shared(); }, {}, {}); ffrt::this_task::sleep_for(2ms); smtx.lock_shared(); EXPECT_EQ(x, 0); smtx.unlock_shared(); smtx.lock(); EXPECT_EQ(x, N); smtx.unlock(); ffrt::wait(); smtx.lock_shared(); EXPECT_EQ(x, N); smtx.unlock_shared(); } static void TryLockSharedTest(ffrt::shared_mutex& smtx) { int x = 0; const int N = 100; ffrt::submit( [&]() { smtx.lock_shared(); ffrt::this_task::sleep_for(20ms); x = N; smtx.unlock_shared(); }, {}, {}); ffrt::this_task::sleep_for(2ms); bool ret = smtx.try_lock_shared(); EXPECT_EQ(ret, true); EXPECT_EQ(x, 0); if (ret) { smtx.unlock_shared(); } ffrt::wait(); ret = smtx.try_lock_shared(); EXPECT_EQ(ret, true); EXPECT_EQ(x, N); if (ret) { smtx.unlock_shared(); } ffrt::submit( [&]() { smtx.lock(); ffrt::this_task::sleep_for(20ms); x = 0; smtx.unlock(); }, {}, {}); ffrt::this_task::sleep_for(2ms); ret = smtx.try_lock_shared(); EXPECT_EQ(ret, false); EXPECT_EQ(x, N); if (ret) { smtx.unlock_shared(); } ffrt::wait(); ret = smtx.try_lock_shared(); EXPECT_EQ(ret, true); EXPECT_EQ(x, 0); if (ret) { smtx.unlock_shared(); } } HWTEST_F(SyncTest, sharedMutexTest, TestSize.Level1) { ffrt::shared_mutex smtx; LockTest(smtx); TryLockTest(smtx); LockSharedTest(smtx); TryLockSharedTest(smtx); } HWTEST_F(SyncTest, thread1, TestSize.Level1) { auto ThreadFunc1 = [](int a, const int& b) { FFRT_LOGW("a = %d, b = %d", a, b); return 0; }; auto ThreadFunc2 = [](const char* a, const char* b) { FFRT_LOGW("%s %s", a, b); return 0; }; { int value = 0; std::thread th0 {[&value, &ThreadFunc1, &ThreadFunc2] { std::thread th1 {ThreadFunc1, 10, 20}; std::thread th2 {ThreadFunc2, "hello", "ffrt"}; th1.join(); th2.join(); value = 123; FFRT_LOGW("value = %d", value); }}; th0.join(); assert(!th0.joinable()); EXPECT_EQ(value, 123); } { int value = 0; ffrt::thread th0 {[&value, &ThreadFunc1, &ThreadFunc2] { ffrt::thread th1 {ThreadFunc1, 10, 20}; ffrt::thread th2 {ThreadFunc2, "hello", "ffrt"}; th1.join(); th2.join(); value = 123; FFRT_LOGW("value = %d", value); }}; th0.join(); assert(!th0.joinable()); EXPECT_EQ(value, 123); } } void f1(int n) { for (int i = 0; i < 5; ++i) { std::cout << "Thread 1 executing\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } void f2(int& n) { for (int i = 0; i < 5; ++i) { std::cout << "Thread 2 executing\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } class foo { public: void bar() { for (int i = 0; i < 5; ++i) { std::cout << "Thread 3 executing\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } int n = 0; }; class baz { public: void operator()() { for (int i = 0; i < 5; ++i) { std::cout << "Thread 4 executing\n"; ++n; std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } int n = 0; }; HWTEST_F(SyncTest, thread2, TestSize.Level1) { { int n = 0; foo f; baz b; { std::thread t2(f1, n + 1); t2.detach(); // test detach } std::thread t1; // t1 is not a thread std::thread t2(f1, n + 1); // pass by value std::thread t3(f2, std::ref(n)); // pass by reference std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f std::thread t6(b); // t6 runs baz::operator() on a copy of object b EXPECT_EQ(t1.joinable(), false); EXPECT_EQ(t2.joinable(), true); t2.join(); EXPECT_EQ(t2.joinable(), false); t4.join(); t5.join(); t6.join(); EXPECT_EQ(n, 5); EXPECT_EQ(f.n, 5); EXPECT_EQ(b.n, 0); } FFRT_LOGW("ffrt version"); { int n = 0; foo f; baz b; { ffrt::thread t2(f1, n + 1); t2.detach(); // test detach } ffrt::thread t1; // t1 is not a thread ffrt::thread t2(f1, n + 1); // pass by value ffrt::thread t3(f2, std::ref(n)); // pass by reference ffrt::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread ffrt::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f ffrt::thread t6(b); // t6 runs baz::operator() on a copy of object b EXPECT_EQ(t1.joinable(), false); EXPECT_EQ(t2.joinable(), true); t2.join(); EXPECT_EQ(t2.joinable(), false); EXPECT_EQ(t3.joinable(), false); EXPECT_EQ(t4.joinable(), true); t4.join(); EXPECT_EQ(t4.joinable(), false); t5.join(); t6.join(); EXPECT_EQ(n, 5); EXPECT_EQ(f.n, 5); EXPECT_EQ(b.n, 0); } } HWTEST_F(SyncTest, thread_with_qos, TestSize.Level1) { int a = 0; auto task = [&] { a++; }; ffrt::thread(static_cast<int>(ffrt::qos_user_initiated), task).join(); EXPECT_EQ(1, a); } HWTEST_F(SyncTest, thread_with_name, TestSize.Level1) { int a = 0; auto task = [&] { a++; }; std::string name = "thread_test"; ffrt::thread(name.c_str(), static_cast<int>(ffrt::qos_user_initiated), task).join(); EXPECT_EQ(1, a); } struct F { template<typename T, typename U> void operator()(T, U, int& a) { using std::is_same; using std::reference_wrapper; static_assert(is_same<T, reference_wrapper<int>>::value, ""); static_assert(is_same<U, reference_wrapper<const int>>::value, ""); a++; } }; HWTEST_F(SyncTest, thread_with_ref_check, TestSize.Level1) { int a = 0; ffrt::thread t(F{}, std::ref(a), std::cref(a), std::ref(a)); t.join(); EXPECT_EQ(1, a); } struct A { A() = default; explicit A(const A&) = default; }; void func(const A&) { } HWTEST_F(SyncTest, thread_with_ref, TestSize.Level1) { ffrt::thread t(func, A{}); t.join(); } HWTEST_F(SyncTest, future_wait, TestSize.Level1) { ffrt::packaged_task<int()> task([] { return 7; }); ffrt::future<int> f1 = task.get_future(); ffrt::thread t(std::move(task)); ffrt::future<int> f2 = ffrt::async([] { return 8; }); ffrt::promise<int> p; ffrt::future<int> f3 = p.get_future(); ffrt::thread([&p] { p.set_value(9); }).detach(); std::cout << "Waiting..." << std::flush; f1.wait(); f2.wait(); f3.wait(); std::cout << "Done!\nResults are: " << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n'; t.join(); }