From 38da7d5d5f6066a53b44b9eaa0efc01aedc99a6a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Dec 2019 11:11:20 -0500 Subject: DRTVWR-476: Add unit tests for LLMainThreadTask. Now that we have the Sync class to help construct unit tests that move forward in a deterministic stepwise order, we can build suitable unit tests for LLMainThreadTask. --- indra/llcommon/tests/llmainthreadtask_test.cpp | 139 +++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 indra/llcommon/tests/llmainthreadtask_test.cpp (limited to 'indra/llcommon/tests/llmainthreadtask_test.cpp') diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp new file mode 100644 index 0000000000..e54c7eda18 --- /dev/null +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -0,0 +1,139 @@ +/** + * @file llmainthreadtask_test.cpp + * @author Nat Goodspeed + * @date 2019-12-05 + * @brief Test for llmainthreadtask. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llmainthreadtask.h" +// STL headers +// std headers +#include +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../test/sync.h" +#include "llthread.h" // on_main_thread() +#include "lleventtimer.h" +#include "lockstatic.h" + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llmainthreadtask_data + { + // 2-second timeout + Sync mSync{F32Milliseconds(2000.0f)}; + + llmainthreadtask_data() + { + // we're not testing the result; this is just to cache the + // initial thread as the main thread. + on_main_thread(); + } + }; + typedef test_group llmainthreadtask_group; + typedef llmainthreadtask_group::object object; + llmainthreadtask_group llmainthreadtaskgrp("llmainthreadtask"); + + template<> template<> + void object::test<1>() + { + set_test_name("inline"); + bool ran = false; + bool result = LLMainThreadTask::dispatch( + [&ran]()->bool{ + ran = true; + return true; + }); + ensure("didn't run lambda", ran); + ensure("didn't return result", result); + } + + struct StaticData + { + std::mutex mMutex; // LockStatic looks for mMutex + bool ran{false}; + }; + typedef llthread::LockStatic LockStatic; + + template<> template<> + void object::test<2>() + { + set_test_name("cross-thread"); + std::atomic_bool result(false); + // wrapping our thread lambda in a packaged_task will catch any + // exceptions it might throw and deliver them via future + std::packaged_task thread_work( + [this, &result](){ + // lock static data first + LockStatic lk; + // unblock test<2>()'s yield_until(1) + mSync.set(1); + // dispatch work to main thread -- should block here + bool on_main( + LLMainThreadTask::dispatch( + lk, // unlock this before blocking! + []()->bool{ + // have to lock static mutex to set static data + LockStatic()->ran = true; + // indicate whether task was run on the main thread + return on_main_thread(); + })); + // wait for test<2>() to unblock us again + mSync.yield_until(3); + result = on_main; + }); + auto thread_result = thread_work.get_future(); + std::thread thread; + try + { + // run thread_work + thread = std::thread(std::move(thread_work)); + // wait for thread to set(1) + mSync.yield_until(1); + // try to acquire the lock, should block because thread has it + LockStatic lk; + // wake up when dispatch() unlocks the static mutex + ensure("shouldn't have run yet", !lk->ran); + ensure("shouldn't have returned yet", !result); + // unlock so the task can acquire the lock + lk.unlock(); + // run the task -- should unblock thread, which will immediately block + // on mSync + LLEventTimer::updateClass(); + // 'lk', having unlocked, can no longer be used to access; relock with + // a new LockStatic instance + ensure("should now have run", LockStatic()->ran); + ensure("returned too early", !result); + // okay, let thread perform the assignment + mSync.set(3); + } + catch (...) + { + // A test failure exception anywhere in the try block can cause + // the test program to terminate without explanation when + // ~thread() finds that 'thread' is still joinable. We could + // either join() or detach() it -- but since it might be blocked + // waiting for something from the main thread that now can never + // happen, it's safer to detach it. + thread.detach(); + throw; + } + // 'thread' should be all done now + thread.join(); + // deliver any exception thrown by thread_work + thread_result.get(); + ensure("ran changed", LockStatic()->ran); + ensure("didn't run on main thread", result); + } +} // namespace tut -- cgit v1.2.3 From fa450ea8492f8d029b781bd911ad270d1b15a3ee Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 13 Dec 2019 14:20:19 -0500 Subject: DRTVWR-476: Update LLMainThreadTask tests for simpler API. --- indra/llcommon/tests/llmainthreadtask_test.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'indra/llcommon/tests/llmainthreadtask_test.cpp') diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index e54c7eda18..8178aa629a 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -75,14 +75,11 @@ namespace tut // exceptions it might throw and deliver them via future std::packaged_task thread_work( [this, &result](){ - // lock static data first - LockStatic lk; // unblock test<2>()'s yield_until(1) mSync.set(1); // dispatch work to main thread -- should block here bool on_main( LLMainThreadTask::dispatch( - lk, // unlock this before blocking! []()->bool{ // have to lock static mutex to set static data LockStatic()->ran = true; -- cgit v1.2.3 From b793ab8619d8c52a099aa85fdd831990ca95c6f4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 26 Mar 2020 17:51:06 -0400 Subject: DRTVWR-476: Apparently it can take more than 2s for threads to chat. llmainthreadtask_test builds in a Sync timeout to keep build-time tests from hanging. That timeout was set to 2000ms, which seems as though it ought to be plenty enough time for a process with only 2 threads to exchange data between them. But on TeamCity EC2 Windows build hosts, sometimes we hit that timeout and fail. Extend it to try to improve the robustness of builds, even though the possibility of a production viewer blocking for that long for anything seems worrisome. (Fortunately the production viewer does not use Sync.) --- indra/llcommon/tests/llmainthreadtask_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/tests/llmainthreadtask_test.cpp') diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index 8178aa629a..d1f455b008 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -31,8 +31,8 @@ namespace tut { struct llmainthreadtask_data { - // 2-second timeout - Sync mSync{F32Milliseconds(2000.0f)}; + // 5-second timeout + Sync mSync{F32Milliseconds(5000.0f)}; llmainthreadtask_data() { -- cgit v1.2.3 From 1d8cbec4c8b6fdc09a2772c4bc98732927323570 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 May 2020 12:44:18 -0400 Subject: DRTVWR-476: LLMainThreadTask cross-thread test hangs. Skip. --- indra/llcommon/tests/llmainthreadtask_test.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon/tests/llmainthreadtask_test.cpp') diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index d1f455b008..69b11ccafb 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -70,6 +70,7 @@ namespace tut void object::test<2>() { set_test_name("cross-thread"); + skip("This test is prone to build-time hangs"); std::atomic_bool result(false); // wrapping our thread lambda in a packaged_task will catch any // exceptions it might throw and deliver them via future -- cgit v1.2.3