summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2019-08-12 17:43:09 -0400
committerNat Goodspeed <nat@lindenlab.com>2019-08-12 17:43:09 -0400
commita5f58663c00d9d6ffa12d4b9f8cf4eeee70b4d5e (patch)
tree4655c10ed8baf9de95732f7bfbb8663e965b2a2a /indra/llcommon
parent3c552696bf8704e30c1525a4f9d4b3dd09034820 (diff)
parent4fce6dc4353dbf9ccd3c9c3aced89df72a4f3abd (diff)
Automated merge with ssh://bitbucket.org/andreykproductengine/drtvwr-493
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/llsingleton.h30
-rw-r--r--indra/llcommon/tests/lleventcoro_test.cpp33
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp83
-rw-r--r--indra/llcommon/tests/lleventfilter_test.cpp12
-rw-r--r--indra/llcommon/tests/llinstancetracker_test.cpp33
-rw-r--r--indra/llcommon/tests/lllazy_test.cpp13
-rw-r--r--indra/llcommon/tests/llleap_test.cpp20
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp16
-rw-r--r--indra/llcommon/tests/llsingleton_test.cpp129
-rw-r--r--indra/llcommon/tests/wrapllerrs.h26
10 files changed, 232 insertions, 163 deletions
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 9be02f7e7d..596a2388a1 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -491,9 +491,6 @@ typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
* * However, distinct initParamSingleton() calls can be used to engage
* different constructors, as long as only one such call is executed at
* runtime.
- * * Circularity is not permitted. No LLSingleton referenced by an
- * LLParamSingleton's constructor or initSingleton() method may call this
- * LLParamSingleton's instance() or getInstance() methods.
* * Unlike LLSingleton, an LLParamSingleton cannot be "revived" by an
* instance() or getInstance() call after deleteSingleton().
*
@@ -508,7 +505,6 @@ private:
public:
using super::deleteSingleton;
- using super::instance;
using super::instanceExists;
using super::wasDeleted;
@@ -519,7 +515,7 @@ public:
// In case racing threads both call initParamSingleton() at the same
// time, serialize them. One should initialize; the other should see
// mInitState already set.
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock<decltype(mMutex)> lk(mMutex);
// For organizational purposes this function shouldn't be called twice
if (super::sData.mInitState != super::UNINITIALIZED)
{
@@ -539,7 +535,7 @@ public:
{
// In case racing threads call getInstance() at the same moment as
// initParamSingleton(), serialize the calls.
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock<decltype(mMutex)> lk(mMutex);
switch (super::sData.mInitState)
{
@@ -551,15 +547,10 @@ public:
case super::CONSTRUCTING:
super::logerrs("Tried to access param singleton ",
super::demangle(typeid(DERIVED_TYPE).name()).c_str(),
- " from singleton constructor!");
+ " from singleton constructor!");
break;
case super::INITIALIZING:
- super::logerrs("Tried to access param singleton ",
- super::demangle(typeid(DERIVED_TYPE).name()).c_str(),
- " from initSingleton() method!");
- break;
-
case super::INITIALIZED:
return super::sData.mInstance;
@@ -574,12 +565,23 @@ public:
return nullptr;
}
+ // instance() is replicated here so it calls
+ // LLParamSingleton::getInstance() rather than LLSingleton::getInstance()
+ // -- avoid making getInstance() virtual
+ static DERIVED_TYPE& instance()
+ {
+ return *getInstance();
+ }
+
private:
- static std::mutex mMutex;
+ // Use a recursive_mutex in case of constructor circularity. With a
+ // non-recursive mutex, that would result in deadlock rather than the
+ // logerrs() call coded above.
+ static std::recursive_mutex mMutex;
};
template<typename T>
-typename std::mutex LLParamSingleton<T>::mMutex;
+typename std::recursive_mutex LLParamSingleton<T>::mMutex;
/**
* Initialization locked singleton, only derived class can decide when to initialize.
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index a459d17fb8..fa02d2bb1a 100644
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -506,16 +506,10 @@ namespace tut
replyName = waiter.getName0();
errorName = waiter.getName1();
WrapLLErrs capture;
- try
- {
- result = waiter.suspendWithLog();
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
+ threw = capture.catch_llerrs([&waiter, &debug](){
+ result = waiter.suspendWithLog();
+ debug("no exception");
+ });
}
END
}
@@ -762,18 +756,13 @@ namespace tut
{
LLCoroEventPumps waiter;
WrapLLErrs capture;
- try
- {
- result = waiter.postAndSuspendWithLog(
- LLSDMap("value", 31)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
+ threw = capture.catch_llerrs(
+ [&waiter, &debug](){
+ result = waiter.postAndSuspendWithLog(
+ LLSDMap("value", 31)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ });
}
END
}
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index 5a4df81bf1..a181d5c941 100644
--- a/indra/llcommon/tests/lleventdispatcher_test.cpp
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -22,6 +22,7 @@
#include "llsdutil.h"
#include "stringize.h"
#include "tests/wrapllerrs.h"
+#include "../test/catch_and_store_what_in.h"
#include <map>
#include <string>
@@ -630,16 +631,9 @@ namespace tut
void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
{
- std::string threw;
- try
- {
- work(func, args);
- }
- catch (const std::runtime_error& e)
- {
- cout << "*** " << e.what() << '\n';
- threw = e.what();
- }
+ std::string threw = catch_what<std::runtime_error>([this, &func, &args](){
+ work(func, args);
+ });
ensure_has(threw, exc_frag);
}
@@ -717,15 +711,9 @@ namespace tut
LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
foreach(LLSD ae, inArray(attempts))
{
- std::string threw;
- try
- {
- work.add("freena_err", "freena", freena, ae);
- }
- catch (const std::exception& e)
- {
- threw = e.what();
- }
+ std::string threw = catch_what<std::exception>([this, &ae](){
+ work.add("freena_err", "freena", freena, ae);
+ });
ensure_has(threw, "must be an array");
}
}
@@ -734,15 +722,9 @@ namespace tut
void object::test<2>()
{
set_test_name("map-style registration with badly-formed defaults");
- std::string threw;
- try
- {
- work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
- }
- catch (const std::exception& e)
- {
- threw = e.what();
- }
+ std::string threw = catch_what<std::exception>([this](){
+ work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
+ });
ensure_has(threw, "must be a map or an array");
}
@@ -750,17 +732,11 @@ namespace tut
void object::test<3>()
{
set_test_name("map-style registration with too many array defaults");
- std::string threw;
- try
- {
- work.add("freena_err", "freena", freena,
- LLSDArray("a")("b"),
- LLSDArray(17)(0.9)("gack"));
- }
- catch (const std::exception& e)
- {
- threw = e.what();
- }
+ std::string threw = catch_what<std::exception>([this](){
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDArray(17)(0.9)("gack"));
+ });
ensure_has(threw, "shorter than");
}
@@ -768,17 +744,11 @@ namespace tut
void object::test<4>()
{
set_test_name("map-style registration with too many map defaults");
- std::string threw;
- try
- {
- work.add("freena_err", "freena", freena,
- LLSDArray("a")("b"),
- LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
- }
- catch (const std::exception& e)
- {
- threw = e.what();
- }
+ std::string threw = catch_what<std::exception>([this](){
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
+ });
ensure_has(threw, "nonexistent params");
ensure_has(threw, "foo");
ensure_has(threw, "bar");
@@ -1039,16 +1009,9 @@ namespace tut
// We don't have a comparable helper function for the one-arg
// operator() method, and it's not worth building one just for this
// case. Write it out.
- std::string threw;
- try
- {
- work(LLSDMap("op", "freek"));
- }
- catch (const std::runtime_error& e)
- {
- cout << "*** " << e.what() << "\n";
- threw = e.what();
- }
+ std::string threw = catch_what<std::runtime_error>([this](){
+ work(LLSDMap("op", "freek"));
+ });
ensure_has(threw, "bad");
ensure_has(threw, "op");
ensure_has(threw, "freek");
diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp
index eb98b12ef5..1875013794 100644
--- a/indra/llcommon/tests/lleventfilter_test.cpp
+++ b/indra/llcommon/tests/lleventfilter_test.cpp
@@ -350,15 +350,9 @@ namespace tut
// Now let the timer expire.
filter.forceTimeout();
// Notice the timeout.
- std::string threw;
- try
- {
- mainloop.post(17);
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- threw = e.what();
- }
+ std::string threw = capture.catch_llerrs([this](){
+ mainloop.post(17);
+ });
ensure_contains("errorAfter() timeout exception", threw, "timeout");
// Timing out cancels the timer. Verify that.
listener0.reset(0);
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index c7d4b8a06b..d94fc0c56d 100644
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -198,14 +198,9 @@ namespace tut
{
WrapLLErrs wrapper;
Keyed::instance_iter i(Keyed::beginInstances());
- try
- {
- delete keyed;
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- what = e.what();
- }
+ what = wrapper.catch_llerrs([&keyed](){
+ delete keyed;
+ });
}
ensure(! what.empty());
}
@@ -219,14 +214,9 @@ namespace tut
{
WrapLLErrs wrapper;
Keyed::key_iter i(Keyed::beginKeys());
- try
- {
- delete keyed;
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- what = e.what();
- }
+ what = wrapper.catch_llerrs([&keyed](){
+ delete keyed;
+ });
}
ensure(! what.empty());
}
@@ -240,14 +230,9 @@ namespace tut
{
WrapLLErrs wrapper;
Unkeyed::instance_iter i(Unkeyed::beginInstances());
- try
- {
- delete unkeyed;
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- what = e.what();
- }
+ what = wrapper.catch_llerrs([&unkeyed](){
+ delete unkeyed;
+ });
}
ensure(! what.empty());
}
diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp
index 32a717f4fc..542306ee22 100644
--- a/indra/llcommon/tests/lllazy_test.cpp
+++ b/indra/llcommon/tests/lllazy_test.cpp
@@ -38,6 +38,7 @@
#include <boost/lambda/bind.hpp>
// other Linden headers
#include "../test/lltut.h"
+#include "../test/catch_and_store_what_in.h"
namespace bll = boost::lambda;
@@ -200,15 +201,9 @@ namespace tut
void lllazy_object::test<2>()
{
TestNeedsTesting tnt;
- std::string threw;
- try
- {
- tnt.toolate();
- }
- catch (const LLLazyCommon::InstanceChange& e)
- {
- threw = e.what();
- }
+ std::string threw = catch_what<LLLazyCommon::InstanceChange>([&tnt](){
+ tnt.toolate();
+ });
ensure_contains("InstanceChange exception", threw, "replace LLLazy instance");
}
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index 45648536c4..bf0a74d10d 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -23,7 +23,7 @@
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "../test/catch_and_store_what_in.h"
-#include "wrapllerrs.h"
+#include "wrapllerrs.h" // CaptureLog
#include "llevents.h"
#include "llprocess.h"
#include "llstring.h"
@@ -290,12 +290,9 @@ namespace tut
void object::test<6>()
{
set_test_name("empty plugin vector");
- std::string threw;
- try
- {
- LLLeap::create("empty", StringVec());
- }
- CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ std::string threw = catch_what<LLLeap::Error>([](){
+ LLLeap::create("empty", StringVec());
+ });
ensure_contains("LLLeap::Error", threw, "no plugin");
// try the suppress-exception variant
ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
@@ -308,12 +305,9 @@ namespace tut
// Synthesize bogus executable name
std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
CaptureLog log;
- std::string threw;
- try
- {
- LLLeap::create("bad exe", BADPYTHON);
- }
- CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ std::string threw = catch_what<LLLeap::Error>([&BADPYTHON](){
+ LLLeap::create("bad exe", BADPYTHON);
+ });
ensure_contains("LLLeap::create() didn't throw", threw, "failed");
log.messageWith("failed");
log.messageWith(BADPYTHON);
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 5c87cdabd9..222d832084 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -25,8 +25,6 @@
#include <boost/function.hpp>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/finder.hpp>
-//#include <boost/lambda/lambda.hpp>
-//#include <boost/lambda/bind.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
@@ -35,7 +33,7 @@
#include "llsdutil.h"
#include "llevents.h"
#include "llstring.h"
-#include "wrapllerrs.h"
+#include "wrapllerrs.h" // CaptureLog
#if defined(LL_WINDOWS)
#define sleep(secs) _sleep((secs) * 1000)
@@ -45,8 +43,7 @@
#include <sys/wait.h>
#endif
-//namespace lambda = boost::lambda;
- std::string apr_strerror_helper(apr_status_t rv)
+std::string apr_strerror_helper(apr_status_t rv)
{
char errbuf[256];
apr_strerror(rv, errbuf, sizeof(errbuf));
@@ -960,12 +957,9 @@ namespace tut
#define CATCH_IN(THREW, EXCEPTION, CODE) \
do \
{ \
- (THREW).clear(); \
- try \
- { \
- CODE; \
- } \
- CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION) \
+ (THREW) = catch_what<EXCEPTION>([&](){ \
+ CODE; \
+ }); \
ensure("failed to throw " #EXCEPTION ": " #CODE, ! (THREW).empty()); \
} while (0)
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
index 56886bc73f..da7bc6355c 100644
--- a/indra/llcommon/tests/llsingleton_test.cpp
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -29,7 +29,8 @@
#include "llsingleton.h"
#include "../test/lltut.h"
-
+#include "wrapllerrs.h"
+#include "llsd.h"
// Capture execution sequence by appending to log string.
std::string sLog;
@@ -198,4 +199,130 @@ namespace tut
TESTS(A, B, 4, 5, 6, 7)
TESTS(B, A, 8, 9, 10, 11)
+
+#define PARAMSINGLETON(cls) \
+ class cls: public LLParamSingleton<cls> \
+ { \
+ LLSINGLETON(cls, const LLSD::String& str): mDesc(str) {} \
+ cls(LLSD::Integer i): mDesc(i) {} \
+ \
+ public: \
+ std::string desc() const { return mDesc.asString(); } \
+ \
+ private: \
+ LLSD mDesc; \
+ }
+
+ // Declare two otherwise-identical LLParamSingleton classes so we can
+ // validly initialize each using two different constructors. If we tried
+ // to test that with a single LLParamSingleton class within the same test
+ // program, we'd get 'trying to use deleted LLParamSingleton' errors.
+ PARAMSINGLETON(PSing1);
+ PARAMSINGLETON(PSing2);
+
+ template<> template<>
+ void singleton_object_t::test<12>()
+ {
+ set_test_name("LLParamSingleton");
+
+ WrapLLErrs catcherr;
+ // query methods
+ ensure("false positive on instanceExists()", ! PSing1::instanceExists());
+ ensure("false positive on wasDeleted()", ! PSing1::wasDeleted());
+ // try to reference before initializing
+ std::string threw = catcherr.catch_llerrs([](){
+ (void)PSing1::instance();
+ });
+ ensure_contains("too-early instance() didn't throw", threw, "Uninitialized");
+ // getInstance() behaves the same as instance()
+ threw = catcherr.catch_llerrs([](){
+ (void)PSing1::getInstance();
+ });
+ ensure_contains("too-early getInstance() didn't throw", threw, "Uninitialized");
+ // initialize using LLSD::String constructor
+ PSing1::initParamSingleton("string");
+ ensure_equals(PSing1::instance().desc(), "string");
+ ensure("false negative on instanceExists()", PSing1::instanceExists());
+ // try to initialize again
+ threw = catcherr.catch_llerrs([](){
+ PSing1::initParamSingleton("again");
+ });
+ ensure_contains("second ctor(string) didn't throw", threw, "twice");
+ // try to initialize using the other constructor -- should be
+ // well-formed, but illegal at runtime
+ threw = catcherr.catch_llerrs([](){
+ PSing1::initParamSingleton(17);
+ });
+ ensure_contains("other ctor(int) didn't throw", threw, "twice");
+ PSing1::deleteSingleton();
+ ensure("false negative on wasDeleted()", PSing1::wasDeleted());
+ threw = catcherr.catch_llerrs([](){
+ (void)PSing1::instance();
+ });
+ ensure_contains("accessed deleted LLParamSingleton", threw, "deleted");
+ }
+
+ template<> template<>
+ void singleton_object_t::test<13>()
+ {
+ set_test_name("LLParamSingleton alternate ctor");
+
+ WrapLLErrs catcherr;
+ // We don't have to restate all the tests for PSing1. Only test validly
+ // using the other constructor.
+ PSing2::initParamSingleton(17);
+ ensure_equals(PSing2::instance().desc(), "17");
+ // can't do it twice
+ std::string threw = catcherr.catch_llerrs([](){
+ PSing2::initParamSingleton(34);
+ });
+ ensure_contains("second ctor(int) didn't throw", threw, "twice");
+ // can't use the other constructor either
+ threw = catcherr.catch_llerrs([](){
+ PSing2::initParamSingleton("string");
+ });
+ ensure_contains("other ctor(string) didn't throw", threw, "twice");
+ }
+
+ class CircularPCtor: public LLParamSingleton<CircularPCtor>
+ {
+ LLSINGLETON(CircularPCtor)
+ {
+ // never mind indirection, just go straight for the circularity
+ (void)instance();
+ }
+ };
+
+ template<> template<>
+ void singleton_object_t::test<14>()
+ {
+ set_test_name("Circular LLParamSingleton constructor");
+ WrapLLErrs catcherr;
+ std::string threw = catcherr.catch_llerrs([](){
+ CircularPCtor::initParamSingleton();
+ });
+ ensure_contains("constructor circularity didn't throw", threw, "constructor");
+ }
+
+ class CircularPInit: public LLParamSingleton<CircularPInit>
+ {
+ LLSINGLETON_EMPTY_CTOR(CircularPInit);
+ public:
+ virtual void initSingleton()
+ {
+ // never mind indirection, just go straight for the circularity
+ (void)instance();
+ }
+ };
+
+ template<> template<>
+ void singleton_object_t::test<15>()
+ {
+ set_test_name("Circular LLParamSingleton initSingleton()");
+ WrapLLErrs catcherr;
+ std::string threw = catcherr.catch_llerrs([](){
+ CircularPInit::initParamSingleton();
+ });
+ ensure("initSingleton() circularity threw", threw.empty());
+ }
}
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 08fbf19b1c..b07d5afbd8 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -37,6 +37,7 @@
#include "llerrorcontrol.h"
#include "llexception.h"
#include "stringize.h"
+#include "../test/catch_and_store_what_in.h"
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
@@ -81,6 +82,31 @@ struct WrapLLErrs
LLTHROW(FatalException(message));
}
+ /// Convenience wrapper for catch_what<FatalException>()
+ //
+ // The implementation makes it clear that this function need not be a
+ // member; it could easily be a free function. It is a member because it
+ // makes no sense to attempt to catch FatalException unless there is a
+ // WrapLLErrs instance in scope. Without a live WrapLLErrs instance, any
+ // LL_ERRS() reached by code within 'func' would terminate the test
+ // program instead of throwing FatalException.
+ //
+ // We were tempted to introduce a free function, likewise accepting
+ // arbitrary 'func', that would instantiate WrapLLErrs and then call
+ // catch_llerrs() on that instance. We decided against it, for this
+ // reason: on extending a test function containing a single call to that
+ // free function, a maintainer would most likely make additional calls to
+ // that free function, instead of switching to an explicit WrapLLErrs
+ // declaration with several calls to its catch_llerrs() member function.
+ // Even a construct such as WrapLLErrs().catch_llerrs(...) would make the
+ // object declaration more visible; it's not unreasonable to expect a
+ // maintainer to extend that by naming and reusing the WrapLLErrs instance.
+ template <typename FUNC>
+ std::string catch_llerrs(FUNC func)
+ {
+ return catch_what<FatalException>(func);
+ }
+
std::string error;
LLError::SettingsStoragePtr mPriorErrorSettings;
LLError::FatalFunction mPriorFatal;