Skip to content
Snippets Groups Projects
Commit ddb4bc47 authored by Rye Mutt's avatar Rye Mutt :bread:
Browse files

Merge branch 'DRTVWR-596' of https://github.com/secondlife/viewer

parents 30185f15 4c791e09
No related branches found
No related tags found
2 merge requests!3Update to main branch,!2Rebase onto current main branch
Showing
with 1876 additions and 457 deletions
......@@ -83,3 +83,4 @@ web/locale.*
web/secondlife.com.*
/Pipfile.lock
.env
.vscode
......@@ -240,6 +240,7 @@ Ansariel Hiller
SL-18432
SL-19140
SL-4126
SL-20224
Aralara Rajal
Arare Chantilly
CHUIBUG-191
......@@ -929,6 +930,8 @@ LSL Scientist
Lamorna Proctor
Lares Carter
Larry Pixel
Lars Næsbye Christensen
SL-20054
Laurent Bechir
Leal Choche
Lenae Munz
......
......@@ -60,7 +60,6 @@ else()
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Build type. One of: Debug Release RelWithDebInfo" FORCE)
......
......@@ -19,8 +19,10 @@ include(XXHash)
set(llcommon_SOURCE_FILES
apply.cpp
commoncontrol.cpp
indra_constants.cpp
lazyeventapi.cpp
llapp.cpp
llapr.cpp
llassettype.cpp
......@@ -117,12 +119,16 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
always_return.h
apply.h
chrono.h
classic_callback.h
commoncontrol.h
ctype_workaround.h
fix_macros.h
function_types.h
indra_constants.h
lazyeventapi.h
linden_common.h
llalignedarray.h
llapp.h
......@@ -329,9 +335,11 @@ if (LL_TESTS)
#set(TEST_DEBUG on)
set(test_libs llcommon)
LL_ADD_INTEGRATION_TEST(apply "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(classic_callback "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lazyeventapi "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
......
/**
* @file always_return.h
* @author Nat Goodspeed
* @date 2023-01-20
* @brief Call specified callable with arbitrary arguments, but always return
* specified type.
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Copyright (c) 2023, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_ALWAYS_RETURN_H)
#define LL_ALWAYS_RETURN_H
#include <type_traits> // std::enable_if, std::is_convertible
namespace LL
{
#if __cpp_lib_is_invocable >= 201703L // C++17
template <typename CALLABLE, typename... ARGS>
using invoke_result = std::invoke_result<CALLABLE, ARGS...>;
#else // C++14
template <typename CALLABLE, typename... ARGS>
using invoke_result = std::result_of<CALLABLE(ARGS...)>;
#endif // C++14
/**
* AlwaysReturn<T>()(some_function, some_args...) calls
* some_function(some_args...). It is guaranteed to return a value of type
* T, regardless of the return type of some_function(). If some_function()
* returns a type convertible to T, it will convert and return that value.
* Otherwise (notably if some_function() is void), AlwaysReturn returns
* T().
*
* When some_function() returns a type not convertible to T, if
* you want AlwaysReturn to return some T value other than
* default-constructed T(), pass that value to AlwaysReturn's constructor.
*/
template <typename DESIRED>
class AlwaysReturn
{
public:
/// pass explicit default value if other than default-constructed type
AlwaysReturn(const DESIRED& dft=DESIRED()): mDefault(dft) {}
// callable returns a type not convertible to DESIRED, return default
template <typename CALLABLE, typename... ARGS,
typename std::enable_if<
! std::is_convertible<
typename invoke_result<CALLABLE, ARGS...>::type,
DESIRED
>::value,
bool
>::type=true>
DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
{
// discard whatever callable(args) returns
std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
return mDefault;
}
// callable returns a type convertible to DESIRED
template <typename CALLABLE, typename... ARGS,
typename std::enable_if<
std::is_convertible<
typename invoke_result<CALLABLE, ARGS...>::type,
DESIRED
>::value,
bool
>::type=true>
DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
{
return { std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...) };
}
private:
DESIRED mDefault;
};
/**
* always_return<T>(some_function, some_args...) calls
* some_function(some_args...). It is guaranteed to return a value of type
* T, regardless of the return type of some_function(). If some_function()
* returns a type convertible to T, it will convert and return that value.
* Otherwise (notably if some_function() is void), always_return() returns
* T().
*/
template <typename DESIRED, typename CALLABLE, typename... ARGS>
DESIRED always_return(CALLABLE&& callable, ARGS&&... args)
{
return AlwaysReturn<DESIRED>()(std::forward<CALLABLE>(callable),
std::forward<ARGS>(args)...);
}
/**
* make_always_return<T>(some_function) returns a callable which, when
* called with appropriate some_function() arguments, always returns a
* value of type T, regardless of the return type of some_function(). If
* some_function() returns a type convertible to T, the returned callable
* will convert and return that value. Otherwise (notably if
* some_function() is void), the returned callable returns T().
*
* When some_function() returns a type not convertible to T, if
* you want the returned callable to return some T value other than
* default-constructed T(), pass that value to make_always_return() as its
* optional second argument.
*/
template <typename DESIRED, typename CALLABLE>
auto make_always_return(CALLABLE&& callable, const DESIRED& dft=DESIRED())
{
return
[dft, callable = std::forward<CALLABLE>(callable)]
(auto&&... args)
{
return AlwaysReturn<DESIRED>(dft)(callable,
std::forward<decltype(args)>(args)...);
};
}
} // namespace LL
#endif /* ! defined(LL_ALWAYS_RETURN_H) */
/**
* @file apply.cpp
* @author Nat Goodspeed
* @date 2022-12-21
* @brief Implementation for apply.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "apply.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "stringize.h"
void LL::apply_validate_size(size_t size, size_t arity)
{
if (size != arity)
{
LLTHROW(apply_error(stringize("LL::apply(func(", arity, " args), "
"std::vector(", size, " elements))")));
}
}
......@@ -12,8 +12,11 @@
#if ! defined(LL_APPLY_H)
#define LL_APPLY_H
#include "llexception.h"
#include <boost/type_traits/function_traits.hpp>
#include <functional> // std::mem_fn()
#include <tuple>
#include <type_traits> // std::is_member_pointer
namespace LL
{
......@@ -54,20 +57,67 @@ namespace LL
}, \
(ARGS))
#if __cplusplus >= 201703L
/*****************************************************************************
* invoke()
*****************************************************************************/
#if __cpp_lib_invoke >= 201411L
// C++17 implementation
using std::apply;
using std::invoke;
#else // no std::invoke
// Use invoke() to handle pointer-to-method:
// derived from https://stackoverflow.com/a/38288251
template<typename Fn, typename... Args,
typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value,
int>::type = 0 >
auto invoke(Fn&& f, Args&&... args)
{
return std::mem_fn(std::forward<Fn>(f))(std::forward<Args>(args)...);
}
template<typename Fn, typename... Args,
typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value,
int>::type = 0 >
auto invoke(Fn&& f, Args&&... args)
{
return std::forward<Fn>(f)(std::forward<Args>(args)...);
}
#endif // no std::invoke
/*****************************************************************************
* apply(function, tuple); apply(function, array)
*****************************************************************************/
#if __cpp_lib_apply >= 201603L
// C++17 implementation
// We don't just say 'using std::apply;' because that template is too general:
// it also picks up the apply(function, vector) case, which we want to handle
// below.
template <typename CALLABLE, typename... ARGS>
auto apply(CALLABLE&& func, const std::tuple<ARGS...>& args)
{
return std::apply(std::forward<CALLABLE>(func), args);
}
#else // C++14
// Derived from https://stackoverflow.com/a/20441189
// and https://en.cppreference.com/w/cpp/utility/apply
template <typename CALLABLE, typename TUPLE, std::size_t... I>
auto apply_impl(CALLABLE&& func, TUPLE&& args, std::index_sequence<I...>)
template <typename CALLABLE, typename... ARGS, std::size_t... I>
auto apply_impl(CALLABLE&& func, const std::tuple<ARGS...>& args, std::index_sequence<I...>)
{
// We accept const std::tuple& so a caller can construct an tuple on the
// fly. But std::get<I>(const tuple) adds a const qualifier to everything
// it extracts. Get a non-const ref to this tuple so we can extract
// without the extraneous const.
auto& non_const_args{ const_cast<std::tuple<ARGS...>&>(args) };
// call func(unpacked args)
return std::forward<CALLABLE>(func)(std::move(std::get<I>(args))...);
return invoke(std::forward<CALLABLE>(func),
std::forward<ARGS>(std::get<I>(non_const_args))...);
}
template <typename CALLABLE, typename... ARGS>
......@@ -81,6 +131,8 @@ auto apply(CALLABLE&& func, const std::tuple<ARGS...>& args)
std::index_sequence_for<ARGS...>{});
}
#endif // C++14
// per https://stackoverflow.com/a/57510428/5533635
template <typename CALLABLE, typename T, size_t SIZE>
auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
......@@ -88,28 +140,92 @@ auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
return apply(std::forward<CALLABLE>(func), std::tuple_cat(args));
}
/*****************************************************************************
* bind_front()
*****************************************************************************/
// To invoke a non-static member function with a tuple, you need a callable
// that binds your member function with an instance pointer or reference.
// std::bind_front() is perfect: std::bind_front(&cls::method, instance).
// Unfortunately bind_front() only enters the standard library in C++20.
#if __cpp_lib_bind_front >= 201907L
// C++20 implementation
using std::bind_front;
#else // no std::bind_front()
template<typename Fn, typename... Args,
typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value,
int>::type = 0 >
auto bind_front(Fn&& f, Args&&... args)
{
// Don't use perfect forwarding for f or args: we must bind them for later.
return [f, pfx_args=std::make_tuple(args...)]
(auto&&... sfx_args)
{
// Use perfect forwarding for sfx_args because we use them as soon as
// we receive them.
return apply(
f,
std::tuple_cat(pfx_args,
std::make_tuple(std::forward<decltype(sfx_args)>(sfx_args)...)));
};
}
template<typename Fn, typename... Args,
typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value,
int>::type = 0 >
auto bind_front(Fn&& f, Args&&... args)
{
return bind_front(std::mem_fn(std::forward<Fn>(f)), std::forward<Args>(args)...);
}
#endif // C++20 with std::bind_front()
/*****************************************************************************
* apply(function, std::vector)
*****************************************************************************/
// per https://stackoverflow.com/a/28411055/5533635
template <typename CALLABLE, typename T, std::size_t... I>
auto apply_impl(CALLABLE&& func, const std::vector<T>& args, std::index_sequence<I...>)
{
return apply(std::forward<CALLABLE>(func),
std::make_tuple(args[I]...));
}
// produce suitable error if apply(func, vector) is the wrong size for func()
void apply_validate_size(size_t size, size_t arity);
/// possible exception from apply() validation
struct apply_error: public LLException
{
apply_error(const std::string& what): LLException(what) {}
};
template <size_t ARITY, typename CALLABLE, typename T>
auto apply_n(CALLABLE&& func, const std::vector<T>& args)
{
apply_validate_size(args.size(), ARITY);
return apply_impl(std::forward<CALLABLE>(func),
std::make_tuple(std::forward<T>(args[I])...),
I...);
args,
std::make_index_sequence<ARITY>());
}
// this goes beyond C++17 std::apply()
/**
* apply(function, std::vector) goes beyond C++17 std::apply(). For this case
* @a function @emph cannot be variadic: the compiler must know at compile
* time how many arguments to pass. This isn't Python. (But see apply_n() to
* pass a specific number of args to a variadic function.)
*/
template <typename CALLABLE, typename T>
auto apply(CALLABLE&& func, const std::vector<T>& args)
{
// infer arity from the definition of func
constexpr auto arity = boost::function_traits<CALLABLE>::arity;
assert(args.size() == arity);
return apply_impl(std::forward<CALLABLE>(func),
args,
std::make_index_sequence<arity>());
// now that we have a compile-time arity, apply_n() works
return apply_n<arity>(std::forward<CALLABLE>(func), args);
}
#endif // C++14
} // namespace LL
#endif /* ! defined(LL_APPLY_H) */
/**
* @file function_types.h
* @author Nat Goodspeed
* @date 2023-01-20
* @brief Extend boost::function_types to examine boost::function and
* std::function
*
* $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Copyright (c) 2023, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_FUNCTION_TYPES_H)
#define LL_FUNCTION_TYPES_H
#include <boost/function.hpp>
#include <boost/function_types/function_arity.hpp>
#include <functional>
namespace LL
{
template <typename F>
struct function_arity_impl
{
static constexpr auto value = boost::function_types::function_arity<F>::value;
};
template <typename F>
struct function_arity_impl<std::function<F>>
{
static constexpr auto value = function_arity_impl<F>::value;
};
template <typename F>
struct function_arity_impl<boost::function<F>>
{
static constexpr auto value = function_arity_impl<F>::value;
};
template <typename F>
struct function_arity
{
static constexpr auto value = function_arity_impl<typename std::decay<F>::type>::value;
};
} // namespace LL
#endif /* ! defined(LL_FUNCTION_TYPES_H) */
/**
* @file lazyeventapi.cpp
* @author Nat Goodspeed
* @date 2022-06-17
* @brief Implementation for lazyeventapi.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lazyeventapi.h"
// STL headers
// std headers
#include <algorithm> // std::find_if
// external library headers
// other Linden headers
#include "llevents.h"
#include "llsdutil.h"
LL::LazyEventAPIBase::LazyEventAPIBase(
const std::string& name, const std::string& desc, const std::string& field)
{
// populate embedded LazyEventAPIParams instance
mParams.name = name;
mParams.desc = desc;
mParams.field = field;
// mParams.init and mOperations are populated by subsequent add() calls.
// Our raison d'etre: register as an LLEventPumps::PumpFactory
// so obtain() will notice any request for this name and call us.
// Of course, our subclass constructor must finish running (making add()
// calls) before mParams will be fully populated, but we expect that to
// happen well before the first LLEventPumps::obtain(name) call.
mRegistered = LLEventPumps::instance().registerPumpFactory(
name,
[this](const std::string& name){ return construct(name); });
}
LL::LazyEventAPIBase::~LazyEventAPIBase()
{
// If our constructor's registerPumpFactory() call was unsuccessful, that
// probably means somebody else claimed the name first. If that's the
// case, do NOT unregister their name out from under them!
// If this is a static instance being destroyed at process shutdown,
// LLEventPumps will probably have been cleaned up already.
if (mRegistered && ! LLEventPumps::wasDeleted())
{
// unregister the callback to this doomed instance
LLEventPumps::instance().unregisterPumpFactory(mParams.name);
}
}
LLSD LL::LazyEventAPIBase::getMetadata(const std::string& name) const
{
// Since mOperations is a vector rather than a map, just search.
auto found = std::find_if(mOperations.begin(), mOperations.end(),
[&name](const auto& namedesc)
{ return (namedesc.first == name); });
if (found == mOperations.end())
return {};
// LLEventDispatcher() supplements the returned metadata in different
// ways, depending on metadata provided to the specific add() method.
// Don't try to emulate all that. At some point we might consider more
// closely unifying LLEventDispatcher machinery with LazyEventAPI, but for
// now this will have to do.
return llsd::map("name", found->first, "desc", found->second);
}
/**
* @file lazyeventapi.h
* @author Nat Goodspeed
* @date 2022-06-16
* @brief Declaring a static module-scope LazyEventAPI registers a specific
* LLEventAPI for future on-demand instantiation.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LAZYEVENTAPI_H)
#define LL_LAZYEVENTAPI_H
#include "apply.h"
#include "lleventapi.h"
#include "llinstancetracker.h"
#include <boost/signals2/signal.hpp>
#include <string>
#include <tuple>
#include <utility> // std::pair
#include <vector>
namespace LL
{
/**
* Bundle params we want to pass to LLEventAPI's protected constructor. We
* package them this way so a subclass constructor can simply forward an
* opaque reference to the LLEventAPI constructor.
*/
// This is a class instead of a plain struct mostly so when we forward-
// declare it we don't have to remember the distinction.
class LazyEventAPIParams
{
public:
// package the parameters used by the normal LLEventAPI constructor
std::string name, desc, field;
// bundle LLEventAPI::add() calls collected by LazyEventAPI::add(), so
// the special LLEventAPI constructor we engage can "play back" those
// add() calls
boost::signals2::signal<void(LLEventAPI*)> init;
};
/**
* LazyEventAPIBase implements most of the functionality of LazyEventAPI
* (q.v.), but we need the LazyEventAPI template subclass so we can accept
* the specific LLEventAPI subclass type.
*/
// No LLInstanceTracker key: we don't need to find a specific instance,
// LLLeapListener just needs to be able to enumerate all instances.
class LazyEventAPIBase: public LLInstanceTracker<LazyEventAPIBase>
{
public:
LazyEventAPIBase(const std::string& name, const std::string& desc,
const std::string& field);
virtual ~LazyEventAPIBase();
// Do not copy or move: once constructed, LazyEventAPIBase must stay
// put: we bind its instance pointer into a callback.
LazyEventAPIBase(const LazyEventAPIBase&) = delete;
LazyEventAPIBase(LazyEventAPIBase&&) = delete;
LazyEventAPIBase& operator=(const LazyEventAPIBase&) = delete;
LazyEventAPIBase& operator=(LazyEventAPIBase&&) = delete;
// capture add() calls we want to play back on LLEventAPI construction
template <typename... ARGS>
void add(const std::string& name, const std::string& desc, ARGS&&... rest)
{
// capture the metadata separately
mOperations.push_back(std::make_pair(name, desc));
// Use connect_extended() so the lambda is passed its own
// connection.
// apply() can't accept a template per se; it needs a particular
// specialization. Specialize out here to work around a clang bug:
// https://github.com/llvm/llvm-project/issues/41999
auto func{ &LazyEventAPIBase::add_trampoline
<const std::string&, const std::string&, ARGS...> };
// We can't bind an unexpanded parameter pack into a lambda --
// shame really. Instead, capture all our args as a std::tuple and
// then, in the lambda, use apply() to pass to add_trampoline().
auto args{ std::make_tuple(name, desc, std::forward<ARGS>(rest)...) };
mParams.init.connect_extended(
[func, args]
(const boost::signals2::connection& conn, LLEventAPI* instance)
{
// we only need this connection once
conn.disconnect();
// apply() expects a tuple specifying ALL the arguments,
// so prepend instance.
apply(func, std::tuple_cat(std::make_tuple(instance), args));
});
}
// The following queries mimic the LLEventAPI / LLEventDispatcher
// query API.
// Get the string name of the subject LLEventAPI
std::string getName() const { return mParams.name; }
// Get the documentation string
std::string getDesc() const { return mParams.desc; }
// Retrieve the LLSD key we use for dispatching
std::string getDispatchKey() const { return mParams.field; }
// operations
using NameDesc = std::pair<std::string, std::string>;
private:
// metadata that might be queried by LLLeapListener
std::vector<NameDesc> mOperations;
public:
using const_iterator = decltype(mOperations)::const_iterator;
const_iterator begin() const { return mOperations.begin(); }
const_iterator end() const { return mOperations.end(); }
LLSD getMetadata(const std::string& name) const;
protected:
// Params with which to instantiate the companion LLEventAPI subclass
LazyEventAPIParams mParams;
private:
// true if we successfully registered our LLEventAPI on construction
bool mRegistered;
// actually instantiate the companion LLEventAPI subclass
virtual LLEventPump* construct(const std::string& name) = 0;
// Passing an overloaded function to any function that accepts an
// arbitrary callable is a PITB because you have to specify the
// correct overload. What we want is for the compiler to select the
// correct overload, based on the carefully-wrought enable_ifs in
// LLEventDispatcher. This (one and only) add_trampoline() method
// exists solely to pass to LL::apply(). Once add_trampoline() is
// called with the expanded arguments, we hope the compiler will Do
// The Right Thing in selecting the correct LLEventAPI::add()
// overload.
template <typename... ARGS>
static
void add_trampoline(LLEventAPI* instance, ARGS&&... args)
{
instance->add(std::forward<ARGS>(args)...);
}
};
/**
* LazyEventAPI provides a way to register a particular LLEventAPI to be
* instantiated on demand, that is, when its name is passed to
* LLEventPumps::obtain().
*
* Derive your listener from LLEventAPI as usual, with its various
* operation methods, but code your constructor to accept
* <tt>(const LL::LazyEventAPIParams& params)</tt>
* and forward that reference to (the protected)
* <tt>LLEventAPI(const LL::LazyEventAPIParams&)</tt> constructor.
*
* Then derive your listener registrar from
* <tt>LazyEventAPI<your LLEventAPI subclass></tt>. The constructor should
* look very like a traditional LLEventAPI constructor:
*
* * pass (name, desc [, field]) to LazyEventAPI's constructor
* * in the body, make a series of add() calls referencing your LLEventAPI
* subclass methods.
*
* You may use any LLEventAPI::add() methods, that is, any
* LLEventDispatcher::add() methods. But the target methods you pass to
* add() must belong to your LLEventAPI subclass, not the LazyEventAPI
* subclass.
*
* Declare a static instance of your LazyEventAPI listener registrar
* class. When it's constructed at static initialization time, it will
* register your LLEventAPI subclass with LLEventPumps. It will also
* collect metadata for the LLEventAPI and its operations to provide to
* LLLeapListener's introspection queries.
*
* When someone later calls LLEventPumps::obtain() to post an event to
* your LLEventAPI subclass, obtain() will instantiate it using
* LazyEventAPI's name, desc, field and add() calls.
*/
template <class EVENTAPI>
class LazyEventAPI: public LazyEventAPIBase
{
public:
// for subclass constructor to reference handler methods
using listener = EVENTAPI;
LazyEventAPI(const std::string& name, const std::string& desc,
const std::string& field="op"):
// Forward ctor params to LazyEventAPIBase
LazyEventAPIBase(name, desc, field)
{}
private:
LLEventPump* construct(const std::string& /*name*/) override
{
// base class has carefully assembled LazyEventAPIParams embedded
// in this instance, just pass to LLEventAPI subclass constructor
return new EVENTAPI(mParams);
}
};
} // namespace LL
#endif /* ! defined(LL_LAZYEVENTAPI_H) */
......@@ -38,6 +38,12 @@ const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAP
bool gAPRInitialized = false;
int abortfunc(int retcode)
{
LL_WARNS("APR") << "Allocation failure in apr pool with code " << (S32)retcode << LL_ENDL;
return 0;
}
void ll_init_apr()
{
// Initialize APR and create the global pool
......@@ -45,7 +51,7 @@ void ll_init_apr()
if (!gAPRPoolp)
{
apr_pool_create(&gAPRPoolp, NULL);
apr_pool_create_ex(&gAPRPoolp, NULL, abortfunc, NULL);
}
if(!LLAPRFile::sAPRFilePoolp)
......
......@@ -274,6 +274,7 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
catch (std::bad_alloc&)
{
// Out of memory on stack allocation?
printActiveCoroutines();
LL_ERRS("LLCoros") << "Bad memory allocation in LLCoros::launch(" << prefix << ")!" << LL_ENDL;
}
......
......@@ -35,6 +35,7 @@
// external library headers
// other Linden headers
#include "llerror.h"
#include "lazyeventapi.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
......@@ -43,6 +44,13 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s
{
}
LLEventAPI::LLEventAPI(const LL::LazyEventAPIParams& params):
LLEventAPI(params.name, params.desc, params.field)
{
// call initialization functions with our brand-new instance pointer
params.init(this);
}
LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
mResp(seed),
mReq(request),
......
......@@ -35,6 +35,11 @@
#include "llinstancetracker.h"
#include <string>
namespace LL
{
class LazyEventAPIParams;
}
/**
* LLEventAPI not only provides operation dispatch functionality, inherited
* from LLDispatchListener -- it also gives us event API introspection.
......@@ -64,19 +69,6 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener,
/// Get the documentation string
std::string getDesc() const { return mDesc; }
/**
* Publish only selected add() methods from LLEventDispatcher.
* Every LLEventAPI add() @em must have a description string.
*/
template <typename CALLABLE>
void add(const std::string& name,
const std::string& desc,
CALLABLE callable,
const LLSD& required=LLSD())
{
LLEventDispatcher::add(name, desc, callable, required);
}
/**
* Instantiate a Response object in any LLEventAPI subclass method that
* wants to guarantee a reply (if requested) will be sent on exit from the
......@@ -150,16 +142,20 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener,
* @endcode
*/
LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
/**
* set the response to the given data
*/
void setResponse(LLSD const & response){ mResp = response; }
/**
* set the response to the given data
*/
void setResponse(LLSD const & response){ mResp = response; }
LLSD mResp, mReq;
LLSD::String mKey;
};
protected:
// constructor used only by subclasses registered by LazyEventAPI
LLEventAPI(const LL::LazyEventAPIParams&);
private:
std::string mDesc;
};
......
This diff is collapsed.
This diff is collapsed.
......@@ -435,16 +435,61 @@ class LLStoreListener: public LLEventFilter
// generic type-appropriate store through mTarget, construct an
// LLSDParam<T> and store that, thus engaging LLSDParam's custom
// conversions.
mTarget = LLSDParam<T>(llsd::drill(event, mPath));
storeTarget(LLSDParam<T>(llsd::drill(event, mPath)));
return mConsume;
}
private:
// This method disambiguates LLStoreListener<LLSD>. Directly assigning
// some_LLSD_var = LLSDParam<LLSD>(some_LLSD_value);
// is problematic because the compiler has too many choices: LLSD has
// multiple assignment operator overloads, and LLSDParam<LLSD> has a
// templated conversion operator. But LLSDParam<LLSD> can convert to a
// (const LLSD&) parameter, and LLSD::operator=(const LLSD&) works.
void storeTarget(const T& value)
{
mTarget = value;
}
T& mTarget;
const LLSD mPath;
const bool mConsume;
};
/**
* LLVarHolder bundles a target variable of the specified type. We use it as a
* base class so the target variable will be fully constructed by the time a
* subclass constructor tries to pass a reference to some other base class.
*/
template <typename T>
struct LLVarHolder
{
T mVar;
};
/**
* LLCaptureListener isa LLStoreListener that bundles the target variable of
* interest.
*/
template <typename T>
class LLCaptureListener: public LLVarHolder<T>,
public LLStoreListener<T>
{
private:
using holder = LLVarHolder<T>;
using super = LLStoreListener<T>;
public:
LLCaptureListener(const LLSD& path=LLSD(), bool consume=false):
super(*this, holder::mVar, path, consume)
{}
void set(T&& newval=T()) { holder::mVar = std::forward<T>(newval); }
const T& get() const { return holder::mVar; }
operator const T&() { return holder::mVar; }
};
/*****************************************************************************
* LLEventLogProxy
*****************************************************************************/
......
......@@ -68,19 +68,78 @@
LLEventPumps::LLEventPumps():
mFactories
{
{ "LLEventStream", [](const std::string& name, bool tweak)
{ "LLEventStream", [](const std::string& name, bool tweak, const std::string& /*type*/)
{ return new LLEventStream(name, tweak); } },
{ "LLEventMailDrop", [](const std::string& name, bool tweak)
{ "LLEventMailDrop", [](const std::string& name, bool tweak, const std::string& /*type*/)
{ return new LLEventMailDrop(name, tweak); } }
},
mTypes
{
// LLEventStream is the default for obtain(), so even if somebody DOES
// call obtain("placeholder"), this sample entry won't break anything.
{ "placeholder", "LLEventStream" }
// { "placeholder", "LLEventStream" }
}
{}
bool LLEventPumps::registerTypeFactory(const std::string& type, const TypeFactory& factory)
{
auto found = mFactories.find(type);
// can't re-register a TypeFactory for a type name that's already registered
if (found != mFactories.end())
return false;
// doesn't already exist, go ahead and register
mFactories[type] = factory;
return true;
}
void LLEventPumps::unregisterTypeFactory(const std::string& type)
{
auto found = mFactories.find(type);
if (found != mFactories.end())
mFactories.erase(found);
}
bool LLEventPumps::registerPumpFactory(const std::string& name, const PumpFactory& factory)
{
// Do we already have a pump by this name?
if (mPumpMap.find(name) != mPumpMap.end())
return false;
// Do we already have an override for this pump name?
if (mTypes.find(name) != mTypes.end())
return false;
// Leverage the two-level lookup implemented by mTypes (pump name -> type
// name) and mFactories (type name -> factory). We could instead create a
// whole separate (pump name -> factory) map, and look in both; or we
// could change mTypes to (pump name -> factory) and, for typical type-
// based lookups, use a "factory" that looks up the real factory in
// mFactories. But this works, and we don't expect many calls to make() -
// either explicit or implicit via obtain().
// Create a bogus type name extremely unlikely to collide with an actual type.
static std::string nul(1, '\0');
std::string type_name{ nul + name };
mTypes[name] = type_name;
// TypeFactory is called with (name, tweak, type), whereas PumpFactory
// accepts only name. We could adapt with std::bind(), but this lambda
// does the trick.
mFactories[type_name] =
[factory]
(const std::string& name, bool /*tweak*/, const std::string& /*type*/)
{ return factory(name); };
return true;
}
void LLEventPumps::unregisterPumpFactory(const std::string& name)
{
auto tfound = mTypes.find(name);
if (tfound != mTypes.end())
{
auto ffound = mFactories.find(tfound->second);
if (ffound != mFactories.end())
{
mFactories.erase(ffound);
}
mTypes.erase(tfound);
}
}
LLEventPump& LLEventPumps::obtain(const std::string& name)
{
PumpMap::iterator found = mPumpMap.find(name);
......@@ -114,7 +173,7 @@ LLEventPump& LLEventPumps::make(const std::string& name, bool tweak,
// Passing an unrecognized type name is a no-no
LLTHROW(BadType(type));
}
auto newInstance = (found->second)(name, tweak);
auto newInstance = (found->second)(name, tweak, type);
// LLEventPump's constructor implicitly registers each new instance in
// mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
// delete it later.
......
......@@ -260,6 +260,45 @@ class LL_COMMON_API LLEventPumps final : public LLSingleton<LLEventPumps>,
LLEventPump& make(const std::string& name, bool tweak=false,
const std::string& type=std::string());
/// function passed to registerTypeFactory()
typedef std::function<LLEventPump*(const std::string& name, bool tweak, const std::string& type)> TypeFactory;
/**
* Register a TypeFactory for use with make(). When make() is called with
* the specified @a type string, call @a factory(name, tweak, type) to
* instantiate it.
*
* Returns true if successfully registered, false if there already exists
* a TypeFactory for the specified @a type name.
*/
bool registerTypeFactory(const std::string& type, const TypeFactory& factory);
void unregisterTypeFactory(const std::string& type);
/// function passed to registerPumpFactory()
typedef std::function<LLEventPump*(const std::string&)> PumpFactory;
/**
* Register a PumpFactory for use with obtain(). When obtain() is called
* with the specified @a name string, if an LLEventPump with the specified
* @a name doesn't already exist, call @a factory(name) to instantiate it.
*
* Returns true if successfully registered, false if there already exists
* a factory override for the specified @a name.
*
* PumpFactory does not support @a tweak because it's only called when
* <i>that particular</i> @a name is passed to obtain(). Bear in mind that
* <tt>obtain(name)</tt> might still bypass the caller's PumpFactory for a
* couple different reasons:
*
* * registerPumpFactory() returns false because there's already a factory
* override for the specified @name
* * between a successful <tt>registerPumpFactory(name)</tt> call (returns
* true) and a call to <tt>obtain(name)</tt>, someone explicitly
* instantiated an LLEventPump(name), so obtain(name) returned that.
*/
bool registerPumpFactory(const std::string& name, const PumpFactory& factory);
void unregisterPumpFactory(const std::string& name);
/**
* Find the named LLEventPump instance. If it exists post the message to it.
* If the pump does not exist, do nothing.
......@@ -317,13 +356,13 @@ class LL_COMMON_API LLEventPumps final : public LLSingleton<LLEventPumps>,
typedef std::set<LLEventPump*> PumpSet;
PumpSet mOurPumps;
// for make(), map string type name to LLEventPump subclass factory function
typedef std::map<std::string, std::function<LLEventPump*(const std::string&, bool)>> PumpFactories;
typedef std::map<std::string, TypeFactory> TypeFactories;
// Data used by make().
// One might think mFactories and mTypes could reasonably be static. So
// they could -- if not for the fact that make() or obtain() might be
// called before this module's static variables have been initialized.
// This is why we use singletons in the first place.
PumpFactories mFactories;
TypeFactories mFactories;
// for obtain(), map desired string instance name to string type when
// obtain() must create the instance
......
......@@ -332,11 +332,28 @@ class LLLeapImpl: public LLLeap
}
else
{
// The LLSD object we got from our stream contains the keys we
// need.
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
// Block calls to this method; resetting mBlocker unblocks calls
// to the other method.
try
{
// The LLSD object we got from our stream contains the
// keys we need.
LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
}
catch (const std::exception& err)
{
// No plugin should be allowed to crash the viewer by
// driving an exception -- intentionally or not.
LOG_UNHANDLED_EXCEPTION(stringize("handling request ", data));
// Whether or not the plugin added a "reply" key to the
// request, send a reply. We happen to know who originated
// this request, and the reply LLEventPump of interest.
// Not our problem if the plugin ignores the reply event.
data["reply"] = mReplyPump.getName();
sendReply(llsd::map("error",
stringize(LLError::Log::classname(err), ": ", err.what())),
data);
}
// Block calls to this method; resetting mBlocker unblocks
// calls to the other method.
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
// Go check for any more pending events in the buffer.
if (childout.size())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment