1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/**
* @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 <atomic>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/sync.h"
#include "llcallbacklist.h"
#include "llthread.h" // on_main_thread()
#include "lockstatic.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llmainthreadtask_data
{
// 5-second timeout
Sync mSync{F32Milliseconds(5000.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_data> 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<StaticData> LockStatic;
template<> template<>
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
std::packaged_task<void()> thread_work(
[this, &result](){
// unblock test<2>()'s yield_until(1)
mSync.set(1);
// dispatch work to main thread -- should block here
bool on_main(
LLMainThreadTask::dispatch(
[]()->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
LLCallbackList::instance().callFunctions();
// '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
|