summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt53
-rw-r--r--indra/llcommon/StackWalker.cpp8
-rw-r--r--indra/llcommon/always_return.h16
-rw-r--r--indra/llcommon/fsyspath.h47
-rw-r--r--indra/llcommon/hbxxh.cpp4
-rw-r--r--indra/llcommon/indra_constants.h386
-rw-r--r--indra/llcommon/linden_common.h6
-rw-r--r--indra/llcommon/llapp.cpp4
-rw-r--r--indra/llcommon/llapp.h2
-rw-r--r--indra/llcommon/llapr.cpp4
-rw-r--r--indra/llcommon/llapr.h10
-rw-r--r--indra/llcommon/llassettype.cpp17
-rw-r--r--indra/llcommon/llassettype.h2
-rw-r--r--indra/llcommon/llcallbacklist.h9
-rw-r--r--indra/llcommon/llcommon.cpp72
-rw-r--r--indra/llcommon/llcoros.cpp80
-rw-r--r--indra/llcommon/llcoros.h4
-rw-r--r--indra/llcommon/lldate.cpp9
-rw-r--r--indra/llcommon/lldate.h1
-rw-r--r--indra/llcommon/lldeadmantimer.h9
-rw-r--r--indra/llcommon/lldefs.h14
-rw-r--r--indra/llcommon/lldependencies.h14
-rw-r--r--indra/llcommon/lldoubledispatch.h14
-rw-r--r--indra/llcommon/llerror.cpp8
-rw-r--r--indra/llcommon/llerrorcontrol.h4
-rw-r--r--indra/llcommon/lleventcoro.cpp12
-rw-r--r--indra/llcommon/lleventcoro.h5
-rw-r--r--indra/llcommon/lleventdispatcher.h15
-rw-r--r--indra/llcommon/lleventfilter.h7
-rw-r--r--indra/llcommon/llevents.cpp1
-rw-r--r--indra/llcommon/llevents.h4
-rw-r--r--indra/llcommon/llexception.cpp44
-rw-r--r--indra/llcommon/llexception.h114
-rw-r--r--indra/llcommon/llfasttimer.cpp2
-rw-r--r--indra/llcommon/llfasttimer.h70
-rw-r--r--indra/llcommon/llfile.cpp410
-rw-r--r--indra/llcommon/llfile.h108
-rw-r--r--indra/llcommon/llhandle.h6
-rw-r--r--indra/llcommon/llinitdestroyclass.h4
-rw-r--r--indra/llcommon/llinitparam.h35
-rw-r--r--indra/llcommon/llleap.cpp13
-rw-r--r--indra/llcommon/llleaplistener.h5
-rw-r--r--indra/llcommon/llmd5.cpp9
-rw-r--r--indra/llcommon/llmemory.h55
-rw-r--r--indra/llcommon/llmutex.h9
-rw-r--r--indra/llcommon/llpounceable.h12
-rw-r--r--indra/llcommon/llpreprocessor.h38
-rw-r--r--indra/llcommon/llprocess.cpp24
-rw-r--r--indra/llcommon/llprocess.h10
-rw-r--r--indra/llcommon/llprocessor.cpp71
-rw-r--r--indra/llcommon/llprocessor.h15
-rw-r--r--indra/llcommon/llprocinfo.h8
-rw-r--r--indra/llcommon/llprofiler.h56
-rw-r--r--indra/llcommon/llptrto.cpp39
-rw-r--r--indra/llcommon/llptrto.h8
-rw-r--r--indra/llcommon/llqueuedthread.cpp34
-rw-r--r--indra/llcommon/llqueuedthread.h6
-rw-r--r--indra/llcommon/llrefcount.h1
-rw-r--r--indra/llcommon/llsdjson.cpp4
-rw-r--r--indra/llcommon/llsdparam.cpp11
-rw-r--r--indra/llcommon/llsdparam.h4
-rw-r--r--indra/llcommon/llsdserialize.cpp2
-rw-r--r--indra/llcommon/llsdserialize_xml.cpp2
-rw-r--r--indra/llcommon/llsdutil.h94
-rw-r--r--indra/llcommon/llsingleton.h6
-rw-r--r--indra/llcommon/llstring.cpp2
-rw-r--r--indra/llcommon/llstring.h8
-rw-r--r--indra/llcommon/llsys.cpp26
-rw-r--r--indra/llcommon/llthread.cpp109
-rw-r--r--indra/llcommon/llthread.h7
-rw-r--r--indra/llcommon/llthreadsafequeue.h6
-rw-r--r--indra/llcommon/lltreeiterators.h10
-rw-r--r--indra/llcommon/lluriparser.cpp2
-rw-r--r--indra/llcommon/lluuid.cpp15
-rw-r--r--indra/llcommon/lluuid.h2
-rw-r--r--indra/llcommon/llwatchdog.cpp290
-rw-r--r--indra/llcommon/llwatchdog.h118
-rw-r--r--indra/llcommon/llwin32headers.h1
-rw-r--r--indra/llcommon/stdtypes.h6
-rw-r--r--indra/llcommon/tests/lldependencies_test.cpp70
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp1
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp63
-rw-r--r--indra/llcommon/tests/llstring_test.cpp50
-rw-r--r--indra/llcommon/tests/lltreeiterators_test.cpp3
-rw-r--r--indra/llcommon/tests/stringize_test.cpp12
-rw-r--r--indra/llcommon/threadpool.h4
-rw-r--r--indra/llcommon/workqueue.cpp140
-rw-r--r--indra/llcommon/workqueue.h17
88 files changed, 2076 insertions, 1056 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 8e56e6a800..2bafef5d1e 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -8,13 +8,9 @@ include(bugsplat)
include(Linking)
include(Boost)
include(LLSharedLibs)
-if (USE_AUTOBUILD_3P OR USE_CONAN)
-include(Copy3rdPartyLibs)
-endif ()
include(ZLIBNG)
include(Tracy)
-
set(llcommon_SOURCE_FILES
apply.cpp
commoncontrol.cpp
@@ -103,6 +99,7 @@ set(llcommon_SOURCE_FILES
lluri.cpp
lluriparser.cpp
lluuid.cpp
+ llwatchdog.cpp
llworkerthread.cpp
hbxxh.cpp
u64.cpp
@@ -244,6 +241,7 @@ set(llcommon_HEADER_FILES
lluri.h
lluriparser.h
lluuid.h
+ llwatchdog.h
llwin32headers.h
llworkerthread.h
hbxxh.h
@@ -288,7 +286,7 @@ target_link_libraries(
target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(llcommon PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-if (CMAKE_OSX_ARCHITECTURES MATCHES arm64)
+if (CMAKE_OSX_ARCHITECTURES MATCHES arm64 OR CMAKE_SYSTEM_PROCESSOR MATCHES aarch64)
if (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/sse2neon_installed OR NOT ${sse2neon_installed} EQUAL 0)
file(MAKE_DIRECTORY ${LIBS_PREBUILT_DIR}/include/sse2neon)
if (NOT EXISTS ${LIBS_PREBUILT_DIR}/include/sse2neon/sse2neon.h)
@@ -296,31 +294,34 @@ if (CMAKE_OSX_ARCHITECTURES MATCHES arm64)
https://raw.githubusercontent.com/DLTcollab/sse2neon/master/sse2neon.h
${LIBS_PREBUILT_DIR}/include/sse2neon/sse2neon.h
)
- endif (NOT EXISTS ${LIBS_PREBUILT_DIR}/include/sse2neon/sse2neon.h)
+ endif ()
file(WRITE ${PREBUILD_TRACKING_DIR}/sse2neon_installed "0")
- endif (${PREBUILD_TRACKING_DIR}/sentinel_installed IS_NEWER_THAN ${PREBUILD_TRACKING_DIR}/sse2neon_installed OR NOT ${sse2neon_installed} EQUAL 0)
+ endif ()
target_include_directories(llcommon PUBLIC ${LIBS_PREBUILT_DIR}/include/sse2neon)
-elseif (LINUX)
+elseif ($ENV{MSYSTEM_CARCH} MATCHES aarch64)
+ target_include_directories(llcommon PUBLIC ${prefix_result}/../include/sse2neon)
+endif ()
+
+if (${LINUX_DISTRO} MATCHES debian OR (${LINUX_DISTRO} MATCHES ubuntu) OR (${LINUX_DISTRO} MATCHES opensuse-tumbleweed))
target_include_directories(llcommon PUBLIC ${LIBS_PREBUILT_DIR}/include)
-endif (CMAKE_OSX_ARCHITECTURES MATCHES arm64)
+endif ()
-if (USE_AUTOBUILD_3P OR USE_CONAN)
-add_dependencies(llcommon stage_third_party_libs)
-else ()
- target_compile_options(${PROJECT_NAME} PUBLIC -Wno-deprecated-declarations)
- if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
- set_source_files_properties(
- llapp.cpp
- llsdutil.cpp
- PROPERTIES COMPILE_FLAGS -Wno-stringop-truncation)
- set_source_files_properties(
- llevent.cpp
- llfasttimer.cpp
- PROPERTIES COMPILE_FLAGS -Wno-nonnull)
- elseif (LINUX AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- set_source_files_properties(llsys.cpp PROPERTIES
- COMPILE_FLAGS -Wno-unused-but-set-variable)
- endif()
+if (NOT WINDOWS)
+ target_compile_options(${PROJECT_NAME} PUBLIC -Wno-deprecated-declarations)
+endif ()
+
+if (CMAKE_CXX_COMPILER_ID MATCHES GNU)
+ set_source_files_properties(
+ llapp.cpp
+ llsdutil.cpp
+ PROPERTIES COMPILE_FLAGS -Wno-stringop-truncation)
+ set_source_files_properties(
+ llevent.cpp
+ llfasttimer.cpp
+ PROPERTIES COMPILE_FLAGS -Wno-nonnull)
+elseif (LINUX AND CMAKE_CXX_COMPILER_ID MATCHES Clang)
+ set_source_files_properties(llsys.cpp PROPERTIES
+ COMPILE_FLAGS -Wno-unused-but-set-variable)
endif ()
include(LibraryInstall)
diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp
index e9ae1723fb..027df80df5 100644
--- a/indra/llcommon/StackWalker.cpp
+++ b/indra/llcommon/StackWalker.cpp
@@ -1100,6 +1100,14 @@ bool StackWalker::ShowCallstack(bool verbose, HANDLE hThread, const CONTEXT *con
s.AddrBStore.Mode = AddrModeFlat;
s.AddrStack.Offset = c.IntSp;
s.AddrStack.Mode = AddrModeFlat;
+#elif _M_ARM64
+ imageType = IMAGE_FILE_MACHINE_ARM64;
+ s.AddrPC.Offset = c.Pc;
+ s.AddrPC.Mode = AddrModeFlat;
+ s.AddrFrame.Offset = c.Fp;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrStack.Offset = c.Sp;
+ s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h
index b99eb49096..a206471da5 100644
--- a/indra/llcommon/always_return.h
+++ b/indra/llcommon/always_return.h
@@ -79,22 +79,6 @@ namespace LL
DESIRED mDefault;
};
- // specialize for AlwaysReturn<void>
- template <>
- struct AlwaysReturn<void>
- {
- public:
- AlwaysReturn() {}
-
- // callable returns a type not convertible to DESIRED, return default
- template <typename CALLABLE, typename... ARGS>
- void operator()(CALLABLE&& callable, ARGS&&... args)
- {
- // discard whatever callable(args) returns
- std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
- }
- };
-
/**
* always_return<T>(some_function, some_args...) calls
* some_function(some_args...). It is guaranteed to return a value of type
diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h
index e9c96edce3..2c900c02a7 100644
--- a/indra/llcommon/fsyspath.h
+++ b/indra/llcommon/fsyspath.h
@@ -12,7 +12,10 @@
#if ! defined(LL_FSYSPATH_H)
#define LL_FSYSPATH_H
+#include <boost/iterator/transform_iterator.hpp>
#include <filesystem>
+#include <string>
+#include <string_view>
// While std::filesystem::path can be directly constructed from std::string on
// both Posix and Windows, that's not what we want on Windows. Per
@@ -33,37 +36,43 @@
// char"), the "native narrow encoding" isn't UTF-8, so file paths containing
// non-ASCII characters get mangled.
//
-// Once we're building with C++20, we could pass a UTF-8 std::string through a
-// vector<char8_t> to engage std::filesystem::path's own UTF-8 conversion. But
-// sigh, as of 2024-04-03 we're not yet there.
-//
-// Anyway, encapsulating the important UTF-8 conversions in our own subclass
-// allows us to migrate forward to C++20 conventions without changing
-// referencing code.
+// Encapsulating the important UTF-8 conversions in our own subclass allows us
+// to migrate forward to C++20 conventions without changing referencing code.
class fsyspath: public std::filesystem::path
{
using super = std::filesystem::path;
+ // In C++20 (__cpp_lib_char8_t), std::filesystem::u8path() is deprecated.
+ // std::filesystem::path(iter, iter) performs UTF-8 conversions when the
+ // value_type of the iterators is char8_t. While we could copy into a
+ // temporary std::u8string and from there into std::filesystem::path, to
+ // minimize string copying we'll define a transform_iterator that accepts
+ // a std::string_view::iterator and dereferences to char8_t.
+ struct u8ify
+ {
+ char8_t operator()(char c) const { return char8_t(c); }
+ };
+ using u8iter = boost::transform_iterator<u8ify, std::string_view::iterator>;
+
public:
// default
fsyspath() {}
- // construct from UTF-8 encoded std::string
- fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {}
- // construct from UTF-8 encoded const char*
- fsyspath(const char* path): super(std::filesystem::u8path(path)) {}
+ // construct from UTF-8 encoded string
+ fsyspath(const std::string& path): fsyspath(std::string_view(path)) {}
+ fsyspath(const char* path): fsyspath(std::string_view(path)) {}
+ fsyspath(std::string_view path):
+ super(u8iter(path.begin(), u8ify()), u8iter(path.end(), u8ify()))
+ {}
// construct from existing path
fsyspath(const super& path): super(path) {}
- fsyspath& operator=(const super& p) { super::operator=(p); return *this; }
- fsyspath& operator=(const std::string& p)
- {
- super::operator=(std::filesystem::u8path(p));
- return *this;
- }
- fsyspath& operator=(const char* p)
+ fsyspath& operator=(const super& p) { super::operator=(p); return *this; }
+ fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); }
+ fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); }
+ fsyspath& operator=(std::string_view p)
{
- super::operator=(std::filesystem::u8path(p));
+ assign(u8iter(p.begin(), u8ify()), u8iter(p.end(), u8ify()));
return *this;
}
diff --git a/indra/llcommon/hbxxh.cpp b/indra/llcommon/hbxxh.cpp
index 8ccf8dfcbe..decf908bb6 100644
--- a/indra/llcommon/hbxxh.cpp
+++ b/indra/llcommon/hbxxh.cpp
@@ -34,11 +34,7 @@
// in your build, in which case the latter would be used instead. For ARM64
// builds, this would also automatically enable NEON vectorization.
#define XXH_INLINE_ALL
-#if LL_USESYSTEMLIBS
#include <xxhash.h>
-#else
-#include "xxhash/xxhash.h"
-#endif
#include "hbxxh.h"
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index d2de88ff0a..a0394da281 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -31,15 +31,15 @@
class LLUUID;
-static const F32 REGION_WIDTH_METERS = 256.f;
-static const S32 REGION_WIDTH_UNITS = 256;
-static const U32 REGION_WIDTH_U32 = 256;
+static constexpr F32 REGION_WIDTH_METERS = 256.f;
+static constexpr S32 REGION_WIDTH_UNITS = 256;
+static constexpr U32 REGION_WIDTH_U32 = 256;
-const F32 REGION_HEIGHT_METERS = 4096.f;
+constexpr F32 REGION_HEIGHT_METERS = 4096.f;
-const F32 DEFAULT_AGENT_DEPTH = 0.45f;
-const F32 DEFAULT_AGENT_WIDTH = 0.60f;
-const F32 DEFAULT_AGENT_HEIGHT = 1.9f;
+constexpr F32 DEFAULT_AGENT_DEPTH = 0.45f;
+constexpr F32 DEFAULT_AGENT_WIDTH = 0.60f;
+constexpr F32 DEFAULT_AGENT_HEIGHT = 1.9f;
enum ETerrainBrushType
{
@@ -67,112 +67,112 @@ enum EMouseClickType{
// keys
// Bit masks for various keyboard modifier keys.
-const MASK MASK_NONE = 0x0000;
-const MASK MASK_CONTROL = 0x0001; // Mapped to cmd on Macs
-const MASK MASK_ALT = 0x0002;
-const MASK MASK_SHIFT = 0x0004;
-const MASK MASK_NORMALKEYS = 0x0007; // A real mask - only get the bits for normal modifier keys
-const MASK MASK_MAC_CONTROL = 0x0008; // Un-mapped Ctrl key on Macs, not used on Windows
-const MASK MASK_MODIFIERS = MASK_CONTROL|MASK_ALT|MASK_SHIFT|MASK_MAC_CONTROL;
+constexpr MASK MASK_NONE = 0x0000;
+constexpr MASK MASK_CONTROL = 0x0001; // Mapped to cmd on Macs
+constexpr MASK MASK_ALT = 0x0002;
+constexpr MASK MASK_SHIFT = 0x0004;
+constexpr MASK MASK_NORMALKEYS = 0x0007; // A real mask - only get the bits for normal modifier keys
+constexpr MASK MASK_MAC_CONTROL = 0x0008; // Un-mapped Ctrl key on Macs, not used on Windows
+constexpr MASK MASK_MODIFIERS = MASK_CONTROL|MASK_ALT|MASK_SHIFT|MASK_MAC_CONTROL;
// Special keys go into >128
-const KEY KEY_SPECIAL = 0x80; // special keys start here
-const KEY KEY_RETURN = 0x81;
-const KEY KEY_LEFT = 0x82;
-const KEY KEY_RIGHT = 0x83;
-const KEY KEY_UP = 0x84;
-const KEY KEY_DOWN = 0x85;
-const KEY KEY_ESCAPE = 0x86;
-const KEY KEY_BACKSPACE =0x87;
-const KEY KEY_DELETE = 0x88;
-const KEY KEY_SHIFT = 0x89;
-const KEY KEY_CONTROL = 0x8A;
-const KEY KEY_ALT = 0x8B;
-const KEY KEY_HOME = 0x8C;
-const KEY KEY_END = 0x8D;
-const KEY KEY_PAGE_UP = 0x8E;
-const KEY KEY_PAGE_DOWN = 0x8F;
-const KEY KEY_HYPHEN = 0x90;
-const KEY KEY_EQUALS = 0x91;
-const KEY KEY_INSERT = 0x92;
-const KEY KEY_CAPSLOCK = 0x93;
-const KEY KEY_TAB = 0x94;
-const KEY KEY_ADD = 0x95;
-const KEY KEY_SUBTRACT =0x96;
-const KEY KEY_MULTIPLY =0x97;
-const KEY KEY_DIVIDE = 0x98;
-const KEY KEY_F1 = 0xA1;
-const KEY KEY_F2 = 0xA2;
-const KEY KEY_F3 = 0xA3;
-const KEY KEY_F4 = 0xA4;
-const KEY KEY_F5 = 0xA5;
-const KEY KEY_F6 = 0xA6;
-const KEY KEY_F7 = 0xA7;
-const KEY KEY_F8 = 0xA8;
-const KEY KEY_F9 = 0xA9;
-const KEY KEY_F10 = 0xAA;
-const KEY KEY_F11 = 0xAB;
-const KEY KEY_F12 = 0xAC;
+constexpr KEY KEY_SPECIAL = 0x80; // special keys start here
+constexpr KEY KEY_RETURN = 0x81;
+constexpr KEY KEY_LEFT = 0x82;
+constexpr KEY KEY_RIGHT = 0x83;
+constexpr KEY KEY_UP = 0x84;
+constexpr KEY KEY_DOWN = 0x85;
+constexpr KEY KEY_ESCAPE = 0x86;
+constexpr KEY KEY_BACKSPACE =0x87;
+constexpr KEY KEY_DELETE = 0x88;
+constexpr KEY KEY_SHIFT = 0x89;
+constexpr KEY KEY_CONTROL = 0x8A;
+constexpr KEY KEY_ALT = 0x8B;
+constexpr KEY KEY_HOME = 0x8C;
+constexpr KEY KEY_END = 0x8D;
+constexpr KEY KEY_PAGE_UP = 0x8E;
+constexpr KEY KEY_PAGE_DOWN = 0x8F;
+constexpr KEY KEY_HYPHEN = 0x90;
+constexpr KEY KEY_EQUALS = 0x91;
+constexpr KEY KEY_INSERT = 0x92;
+constexpr KEY KEY_CAPSLOCK = 0x93;
+constexpr KEY KEY_TAB = 0x94;
+constexpr KEY KEY_ADD = 0x95;
+constexpr KEY KEY_SUBTRACT =0x96;
+constexpr KEY KEY_MULTIPLY =0x97;
+constexpr KEY KEY_DIVIDE = 0x98;
+constexpr KEY KEY_F1 = 0xA1;
+constexpr KEY KEY_F2 = 0xA2;
+constexpr KEY KEY_F3 = 0xA3;
+constexpr KEY KEY_F4 = 0xA4;
+constexpr KEY KEY_F5 = 0xA5;
+constexpr KEY KEY_F6 = 0xA6;
+constexpr KEY KEY_F7 = 0xA7;
+constexpr KEY KEY_F8 = 0xA8;
+constexpr KEY KEY_F9 = 0xA9;
+constexpr KEY KEY_F10 = 0xAA;
+constexpr KEY KEY_F11 = 0xAB;
+constexpr KEY KEY_F12 = 0xAC;
-const KEY KEY_PAD_UP = 0xC0;
-const KEY KEY_PAD_DOWN = 0xC1;
-const KEY KEY_PAD_LEFT = 0xC2;
-const KEY KEY_PAD_RIGHT = 0xC3;
-const KEY KEY_PAD_HOME = 0xC4;
-const KEY KEY_PAD_END = 0xC5;
-const KEY KEY_PAD_PGUP = 0xC6;
-const KEY KEY_PAD_PGDN = 0xC7;
-const KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle
-const KEY KEY_PAD_INS = 0xC9;
-const KEY KEY_PAD_DEL = 0xCA;
-const KEY KEY_PAD_RETURN = 0xCB;
-const KEY KEY_PAD_ADD = 0xCC; // not used
-const KEY KEY_PAD_SUBTRACT = 0xCD; // not used
-const KEY KEY_PAD_MULTIPLY = 0xCE; // not used
-const KEY KEY_PAD_DIVIDE = 0xCF; // not used
+constexpr KEY KEY_PAD_UP = 0xC0;
+constexpr KEY KEY_PAD_DOWN = 0xC1;
+constexpr KEY KEY_PAD_LEFT = 0xC2;
+constexpr KEY KEY_PAD_RIGHT = 0xC3;
+constexpr KEY KEY_PAD_HOME = 0xC4;
+constexpr KEY KEY_PAD_END = 0xC5;
+constexpr KEY KEY_PAD_PGUP = 0xC6;
+constexpr KEY KEY_PAD_PGDN = 0xC7;
+constexpr KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle
+constexpr KEY KEY_PAD_INS = 0xC9;
+constexpr KEY KEY_PAD_DEL = 0xCA;
+constexpr KEY KEY_PAD_RETURN = 0xCB;
+constexpr KEY KEY_PAD_ADD = 0xCC; // not used
+constexpr KEY KEY_PAD_SUBTRACT = 0xCD; // not used
+constexpr KEY KEY_PAD_MULTIPLY = 0xCE; // not used
+constexpr KEY KEY_PAD_DIVIDE = 0xCF; // not used
-const KEY KEY_BUTTON0 = 0xD0;
-const KEY KEY_BUTTON1 = 0xD1;
-const KEY KEY_BUTTON2 = 0xD2;
-const KEY KEY_BUTTON3 = 0xD3;
-const KEY KEY_BUTTON4 = 0xD4;
-const KEY KEY_BUTTON5 = 0xD5;
-const KEY KEY_BUTTON6 = 0xD6;
-const KEY KEY_BUTTON7 = 0xD7;
-const KEY KEY_BUTTON8 = 0xD8;
-const KEY KEY_BUTTON9 = 0xD9;
-const KEY KEY_BUTTON10 = 0xDA;
-const KEY KEY_BUTTON11 = 0xDB;
-const KEY KEY_BUTTON12 = 0xDC;
-const KEY KEY_BUTTON13 = 0xDD;
-const KEY KEY_BUTTON14 = 0xDE;
-const KEY KEY_BUTTON15 = 0xDF;
+constexpr KEY KEY_BUTTON0 = 0xD0;
+constexpr KEY KEY_BUTTON1 = 0xD1;
+constexpr KEY KEY_BUTTON2 = 0xD2;
+constexpr KEY KEY_BUTTON3 = 0xD3;
+constexpr KEY KEY_BUTTON4 = 0xD4;
+constexpr KEY KEY_BUTTON5 = 0xD5;
+constexpr KEY KEY_BUTTON6 = 0xD6;
+constexpr KEY KEY_BUTTON7 = 0xD7;
+constexpr KEY KEY_BUTTON8 = 0xD8;
+constexpr KEY KEY_BUTTON9 = 0xD9;
+constexpr KEY KEY_BUTTON10 = 0xDA;
+constexpr KEY KEY_BUTTON11 = 0xDB;
+constexpr KEY KEY_BUTTON12 = 0xDC;
+constexpr KEY KEY_BUTTON13 = 0xDD;
+constexpr KEY KEY_BUTTON14 = 0xDE;
+constexpr KEY KEY_BUTTON15 = 0xDF;
-const KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only.
+constexpr KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only.
-const S32 KEY_COUNT = 256;
+constexpr S32 KEY_COUNT = 256;
-const F32 DEFAULT_WATER_HEIGHT = 20.0f;
+constexpr F32 DEFAULT_WATER_HEIGHT = 20.0f;
// Maturity ratings for simulators
-const U8 SIM_ACCESS_MIN = 0; // Treated as 'unknown', usually ends up being SIM_ACCESS_PG
-const U8 SIM_ACCESS_PG = 13;
-const U8 SIM_ACCESS_MATURE = 21;
-const U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only
-const U8 SIM_ACCESS_DOWN = 254;
-const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT;
+constexpr U8 SIM_ACCESS_MIN = 0; // Treated as 'unknown', usually ends up being SIM_ACCESS_PG
+constexpr U8 SIM_ACCESS_PG = 13;
+constexpr U8 SIM_ACCESS_MATURE = 21;
+constexpr U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only
+constexpr U8 SIM_ACCESS_DOWN = 254;
+constexpr U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT;
// attachment constants
-const U8 ATTACHMENT_ADD = 0x80;
+constexpr U8 ATTACHMENT_ADD = 0x80;
// god levels
-const U8 GOD_MAINTENANCE = 250;
-const U8 GOD_FULL = 200;
-const U8 GOD_LIAISON = 150;
-const U8 GOD_CUSTOMER_SERVICE = 100;
-const U8 GOD_LIKE = 1;
-const U8 GOD_NOT = 0;
+constexpr U8 GOD_MAINTENANCE = 250;
+constexpr U8 GOD_FULL = 200;
+constexpr U8 GOD_LIAISON = 150;
+constexpr U8 GOD_CUSTOMER_SERVICE = 100;
+constexpr U8 GOD_LIKE = 1;
+constexpr U8 GOD_NOT = 0;
// "agent id" for things that should be done to ALL agents
LL_COMMON_API extern const LLUUID LL_UUID_ALL_AGENTS;
@@ -239,121 +239,123 @@ LL_COMMON_API extern const LLUUID BLANK_OBJECT_NORMAL;
LL_COMMON_API extern const LLUUID BLANK_MATERIAL_ASSET_ID;
// radius within which a chat message is fully audible
-const F32 CHAT_NORMAL_RADIUS = 20.f;
+constexpr F32 CHAT_NORMAL_RADIUS = 20.f;
// media commands
-const U32 PARCEL_MEDIA_COMMAND_STOP = 0;
-const U32 PARCEL_MEDIA_COMMAND_PAUSE = 1;
-const U32 PARCEL_MEDIA_COMMAND_PLAY = 2;
-const U32 PARCEL_MEDIA_COMMAND_LOOP = 3;
-const U32 PARCEL_MEDIA_COMMAND_TEXTURE = 4;
-const U32 PARCEL_MEDIA_COMMAND_URL = 5;
-const U32 PARCEL_MEDIA_COMMAND_TIME = 6;
-const U32 PARCEL_MEDIA_COMMAND_AGENT = 7;
-const U32 PARCEL_MEDIA_COMMAND_UNLOAD = 8;
-const U32 PARCEL_MEDIA_COMMAND_AUTO_ALIGN = 9;
-const U32 PARCEL_MEDIA_COMMAND_TYPE = 10;
-const U32 PARCEL_MEDIA_COMMAND_SIZE = 11;
-const U32 PARCEL_MEDIA_COMMAND_DESC = 12;
-const U32 PARCEL_MEDIA_COMMAND_LOOP_SET = 13;
+constexpr U32 PARCEL_MEDIA_COMMAND_STOP = 0;
+constexpr U32 PARCEL_MEDIA_COMMAND_PAUSE = 1;
+constexpr U32 PARCEL_MEDIA_COMMAND_PLAY = 2;
+constexpr U32 PARCEL_MEDIA_COMMAND_LOOP = 3;
+constexpr U32 PARCEL_MEDIA_COMMAND_TEXTURE = 4;
+constexpr U32 PARCEL_MEDIA_COMMAND_URL = 5;
+constexpr U32 PARCEL_MEDIA_COMMAND_TIME = 6;
+constexpr U32 PARCEL_MEDIA_COMMAND_AGENT = 7;
+constexpr U32 PARCEL_MEDIA_COMMAND_UNLOAD = 8;
+constexpr U32 PARCEL_MEDIA_COMMAND_AUTO_ALIGN = 9;
+constexpr U32 PARCEL_MEDIA_COMMAND_TYPE = 10;
+constexpr U32 PARCEL_MEDIA_COMMAND_SIZE = 11;
+constexpr U32 PARCEL_MEDIA_COMMAND_DESC = 12;
+constexpr U32 PARCEL_MEDIA_COMMAND_LOOP_SET = 13;
const S32 CHAT_CHANNEL_DEBUG = S32_MAX;
// agent constants
-const U32 CONTROL_AT_POS_INDEX = 0;
-const U32 CONTROL_AT_NEG_INDEX = 1;
-const U32 CONTROL_LEFT_POS_INDEX = 2;
-const U32 CONTROL_LEFT_NEG_INDEX = 3;
-const U32 CONTROL_UP_POS_INDEX = 4;
-const U32 CONTROL_UP_NEG_INDEX = 5;
-const U32 CONTROL_PITCH_POS_INDEX = 6;
-const U32 CONTROL_PITCH_NEG_INDEX = 7;
-const U32 CONTROL_YAW_POS_INDEX = 8;
-const U32 CONTROL_YAW_NEG_INDEX = 9;
-const U32 CONTROL_FAST_AT_INDEX = 10;
-const U32 CONTROL_FAST_LEFT_INDEX = 11;
-const U32 CONTROL_FAST_UP_INDEX = 12;
-const U32 CONTROL_FLY_INDEX = 13;
-const U32 CONTROL_STOP_INDEX = 14;
-const U32 CONTROL_FINISH_ANIM_INDEX = 15;
-const U32 CONTROL_STAND_UP_INDEX = 16;
-const U32 CONTROL_SIT_ON_GROUND_INDEX = 17;
-const U32 CONTROL_MOUSELOOK_INDEX = 18;
-const U32 CONTROL_NUDGE_AT_POS_INDEX = 19;
-const U32 CONTROL_NUDGE_AT_NEG_INDEX = 20;
-const U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21;
-const U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
-const U32 CONTROL_NUDGE_UP_POS_INDEX = 23;
-const U32 CONTROL_NUDGE_UP_NEG_INDEX = 24;
-const U32 CONTROL_TURN_LEFT_INDEX = 25;
-const U32 CONTROL_TURN_RIGHT_INDEX = 26;
-const U32 CONTROL_AWAY_INDEX = 27;
-const U32 CONTROL_LBUTTON_DOWN_INDEX = 28;
-const U32 CONTROL_LBUTTON_UP_INDEX = 29;
-const U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
-const U32 CONTROL_ML_LBUTTON_UP_INDEX = 31;
-const U32 TOTAL_CONTROLS = 32;
+constexpr U32 CONTROL_AT_POS_INDEX = 0;
+constexpr U32 CONTROL_AT_NEG_INDEX = 1;
+constexpr U32 CONTROL_LEFT_POS_INDEX = 2;
+constexpr U32 CONTROL_LEFT_NEG_INDEX = 3;
+constexpr U32 CONTROL_UP_POS_INDEX = 4;
+constexpr U32 CONTROL_UP_NEG_INDEX = 5;
+constexpr U32 CONTROL_PITCH_POS_INDEX = 6;
+constexpr U32 CONTROL_PITCH_NEG_INDEX = 7;
+constexpr U32 CONTROL_YAW_POS_INDEX = 8;
+constexpr U32 CONTROL_YAW_NEG_INDEX = 9;
+constexpr U32 CONTROL_FAST_AT_INDEX = 10;
+constexpr U32 CONTROL_FAST_LEFT_INDEX = 11;
+constexpr U32 CONTROL_FAST_UP_INDEX = 12;
+constexpr U32 CONTROL_FLY_INDEX = 13;
+constexpr U32 CONTROL_STOP_INDEX = 14;
+constexpr U32 CONTROL_FINISH_ANIM_INDEX = 15;
+constexpr U32 CONTROL_STAND_UP_INDEX = 16;
+constexpr U32 CONTROL_SIT_ON_GROUND_INDEX = 17;
+constexpr U32 CONTROL_MOUSELOOK_INDEX = 18;
+constexpr U32 CONTROL_NUDGE_AT_POS_INDEX = 19;
+constexpr U32 CONTROL_NUDGE_AT_NEG_INDEX = 20;
+constexpr U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21;
+constexpr U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
+constexpr U32 CONTROL_NUDGE_UP_POS_INDEX = 23;
+constexpr U32 CONTROL_NUDGE_UP_NEG_INDEX = 24;
+constexpr U32 CONTROL_TURN_LEFT_INDEX = 25;
+constexpr U32 CONTROL_TURN_RIGHT_INDEX = 26;
+constexpr U32 CONTROL_AWAY_INDEX = 27;
+constexpr U32 CONTROL_LBUTTON_DOWN_INDEX = 28;
+constexpr U32 CONTROL_LBUTTON_UP_INDEX = 29;
+constexpr U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
+constexpr U32 CONTROL_ML_LBUTTON_UP_INDEX = 31;
+constexpr U32 TOTAL_CONTROLS = 32;
-const U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX; // 0x00000001
-const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002
-const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004
-const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008
-const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010
-const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020
-const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040
-const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080
-const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100
-const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200
+constexpr U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX; // 0x00000001
+constexpr U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002
+constexpr U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004
+constexpr U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008
+constexpr U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010
+constexpr U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020
+constexpr U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040
+constexpr U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080
+constexpr U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100
+constexpr U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200
-const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400
-const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800
-const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000
+constexpr U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400
+constexpr U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800
+constexpr U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000
-const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000
-const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000
-const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000
-const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000
-const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000
-const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000
+constexpr U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000
+constexpr U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000
+constexpr U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000
+constexpr U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000
+constexpr U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000
+constexpr U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000
-const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000
-const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000
-const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000
-const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000
-const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000
-const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000
-const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000
-const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000
+constexpr U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000
+constexpr U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000
+constexpr U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000
+constexpr U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000
+constexpr U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000
+constexpr U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000
+constexpr U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000
+constexpr U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000
-const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000
+constexpr U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000
-const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000
-const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000
-const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000
-const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000
+constexpr U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000
+constexpr U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000
+constexpr U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000
+constexpr U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000
// move these up so that we can hide them in "State" for object updates
// (for now)
-const U32 AGENT_ATTACH_OFFSET = 4;
-const U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET;
+constexpr U32 AGENT_ATTACH_OFFSET = 4;
+constexpr U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET;
// RN: this method swaps the upper and lower nibbles to maintain backward
// compatibility with old objects that only used the upper nibble
#define ATTACHMENT_ID_FROM_STATE(state) ((S32)((((U8)state & AGENT_ATTACH_MASK) >> 4) | (((U8)state & ~AGENT_ATTACH_MASK) << 4)))
// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
-const U8 CLICK_ACTION_NONE = 0;
-const U8 CLICK_ACTION_TOUCH = 0;
-const U8 CLICK_ACTION_SIT = 1;
-const U8 CLICK_ACTION_BUY = 2;
-const U8 CLICK_ACTION_PAY = 3;
-const U8 CLICK_ACTION_OPEN = 4;
-const U8 CLICK_ACTION_PLAY = 5;
-const U8 CLICK_ACTION_OPEN_MEDIA = 6;
-const U8 CLICK_ACTION_ZOOM = 7;
-const U8 CLICK_ACTION_DISABLED = 8;
-const U8 CLICK_ACTION_IGNORE = 9;
+constexpr U8 CLICK_ACTION_NONE = 0;
+constexpr U8 CLICK_ACTION_TOUCH = 0;
+constexpr U8 CLICK_ACTION_SIT = 1;
+constexpr U8 CLICK_ACTION_BUY = 2;
+constexpr U8 CLICK_ACTION_PAY = 3;
+constexpr U8 CLICK_ACTION_OPEN = 4;
+constexpr U8 CLICK_ACTION_PLAY = 5;
+constexpr U8 CLICK_ACTION_OPEN_MEDIA = 6;
+constexpr U8 CLICK_ACTION_ZOOM = 7;
+constexpr U8 CLICK_ACTION_DISABLED = 8;
+constexpr U8 CLICK_ACTION_IGNORE = 9;
// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
+constexpr U32 BEACON_SHOW_MAP = 0x0001;
+constexpr U32 BEACON_FOCUS_MAP = 0x0002;
#endif
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
index a918caa2e8..a41af153fe 100644
--- a/indra/llcommon/linden_common.h
+++ b/indra/llcommon/linden_common.h
@@ -28,12 +28,6 @@
#define LL_LINDEN_COMMON_H
#include "llprofiler.h"
-#if TRACY_ENABLE && !defined(LL_PROFILER_ENABLE_TRACY_OPENGL) // hooks for memory profiling
-void *tracy_aligned_malloc(size_t size, size_t alignment);
-void tracy_aligned_free(void *memblock);
-#define _aligned_malloc(X, Y) tracy_aligned_malloc((X), (Y))
-#define _aligned_free(X) tracy_aligned_free((X))
-#endif
// *NOTE: Please keep includes here to a minimum!
//
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 08a43983d3..c532620daa 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -229,7 +229,7 @@ bool LLApp::parseCommandOptions(int argc, wchar_t** wargv)
if(wargv[ii][1] == '-') ++offset;
#if LL_WINDOWS
- name.assign(utf16str_to_utf8str(&wargv[ii][offset]));
+ name.assign(ll_convert_wide_to_string(&wargv[ii][offset]));
#else
name.assign(wstring_to_utf8str(&wargv[ii][offset]));
#endif
@@ -253,7 +253,7 @@ bool LLApp::parseCommandOptions(int argc, wchar_t** wargv)
++ii;
#if LL_WINDOWS
- value.assign(utf16str_to_utf8str((wargv[ii])));
+ value.assign(ll_convert_wide_to_string((wargv[ii])));
#else
value.assign(wstring_to_utf8str((wargv[ii])));
#endif
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index 3d18864b80..57f5a112d9 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -282,7 +282,7 @@ public:
LLRunner& getRunner() { return mRunner; }
#ifdef LL_WINDOWS
- virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { }
+ virtual bool reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { return false; }
#endif
public:
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 01763c49aa..51c814a387 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -154,7 +154,7 @@ LLVolatileAPRPool::LLVolatileAPRPool(bool is_local, apr_pool_t *parent, apr_size
//create mutex
if(!is_local) //not a local apr_pool, that is: shared by multiple threads.
{
- mMutexp.reset(new std::mutex());
+ mMutexp = std::make_unique<std::mutex>();
}
}
@@ -230,7 +230,7 @@ bool LLVolatileAPRPool::isFull()
bool _ll_apr_warn_status(apr_status_t status, const char* file, int line)
{
if(APR_SUCCESS == status) return false;
-#if !LL_LINUX
+#if !LL_LINUX && !__FreeBSD__
char buf[MAX_STRING]; /* Flawfinder: ignore */
apr_strerror(status, buf, sizeof(buf));
LL_WARNS("APR") << "APR: " << file << ":" << line << " " << buf << LL_ENDL;
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 13597d56a4..d9f1a31575 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -33,7 +33,6 @@
#include <sys/param.h> // Need PATH_MAX in APR headers...
#endif
-#include <boost/noncopyable.hpp>
#include "llwin32headers.h"
#include "apr_thread_proc.h"
#include "apr_getopt.h"
@@ -146,7 +145,7 @@ private:
// 2, a global pool.
//
-class LL_COMMON_API LLAPRFile : boost::noncopyable
+class LL_COMMON_API LLAPRFile
{
// make this non copyable since a copy closes the file
private:
@@ -154,9 +153,12 @@ private:
LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
public:
- LLAPRFile() ;
+ LLAPRFile();
LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL);
- ~LLAPRFile() ;
+ ~LLAPRFile();
+
+ LLAPRFile(const LLAPRFile&) = delete;
+ LLAPRFile& operator=(const LLAPRFile&) = delete;
apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL);
apr_status_t open(const std::string& filename, apr_int32_t flags, bool use_global_pool); //use gAPRPoolp.
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index c09cf7abd2..9672a3262b 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -29,6 +29,7 @@
#include "llassettype.h"
#include "lldictionary.h"
#include "llmemory.h"
+#include "llsd.h"
#include "llsingleton.h"
///----------------------------------------------------------------------------
@@ -246,3 +247,19 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)
}
return false;
}
+
+LLSD LLAssetType::getTypeNames()
+{
+ LLSD type_names;
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type)
+ {
+ const AssetEntry *entry = dict->lookup((LLAssetType::EType) type);
+ // skip llassettype_bad_lookup
+ if (entry)
+ {
+ type_names.append(entry->mTypeName);
+ }
+ }
+ return type_names;
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index 547c3f4329..17177d81c3 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -165,6 +165,8 @@ public:
static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download
static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer
+ static LLSD getTypeNames();
+
static const std::string BADLOOKUP;
protected:
diff --git a/indra/llcommon/llcallbacklist.h b/indra/llcommon/llcallbacklist.h
index d6c415f7c5..036e575117 100644
--- a/indra/llcommon/llcallbacklist.h
+++ b/indra/llcommon/llcallbacklist.h
@@ -27,8 +27,9 @@
#ifndef LL_LLCALLBACKLIST_H
#define LL_LLCALLBACKLIST_H
-#include "llstl.h"
-#include <boost/function.hpp>
+#include "stdtypes.h"
+
+#include <functional>
#include <list>
class LLCallbackList
@@ -59,8 +60,8 @@ protected:
callback_list_t mCallbackList;
};
-typedef boost::function<void ()> nullary_func_t;
-typedef boost::function<bool ()> bool_func_t;
+typedef std::function<void ()> nullary_func_t;
+typedef std::function<bool ()> bool_func_t;
// Call a given callable once in idle loop.
void doOnIdleOneTime(nullary_func_t callable);
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index 84b35749cc..7a22eaf203 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -33,23 +33,23 @@
#include "lltracethreadrecorder.h"
#include "llcleanup.h"
-thread_local bool gProfilerEnabled = false;
-
-#if (TRACY_ENABLE)
+#if LL_PROFILER_CONFIGURATION >= LL_PROFILER_CONFIG_TRACY && TRACY_ENABLE
// Override new/delete for tracy memory profiling
void* ll_tracy_new(size_t size)
{
- void* ptr;
- if (gProfilerEnabled)
- {
- //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
- ptr = (malloc)(size);
- }
- else
+ void* ptr = (malloc)(size);
+ if (!ptr)
{
- ptr = (malloc)(size);
+ throw std::bad_alloc();
}
+ LL_PROFILE_ALLOC(ptr, size);
+ return ptr;
+}
+
+void* ll_tracy_aligned_new(size_t size, size_t alignment)
+{
+ void* ptr = ll_aligned_malloc_fallback(size, alignment);
if (!ptr)
{
throw std::bad_alloc();
@@ -58,6 +58,18 @@ void* ll_tracy_new(size_t size)
return ptr;
}
+void ll_tracy_delete(void* ptr)
+{
+ LL_PROFILE_FREE(ptr);
+ (free)(ptr);
+}
+
+void ll_tracy_aligned_delete(void* ptr)
+{
+ LL_PROFILE_FREE(ptr);
+ ll_aligned_free_fallback(ptr);
+}
+
void* operator new(size_t size)
{
return ll_tracy_new(size);
@@ -68,18 +80,14 @@ void* operator new[](std::size_t count)
return ll_tracy_new(count);
}
-void ll_tracy_delete(void* ptr)
+void* operator new(size_t size, std::align_val_t align)
{
- LL_PROFILE_FREE(ptr);
- if (gProfilerEnabled)
- {
- //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
- (free)(ptr);
- }
- else
- {
- (free)(ptr);
- }
+ return ll_tracy_aligned_new(size, (size_t)align);
+}
+
+void* operator new[](std::size_t count, std::align_val_t align)
+{
+ return ll_tracy_aligned_new(count, (size_t)align);
}
void operator delete(void *ptr) noexcept
@@ -92,27 +100,17 @@ void operator delete[](void* ptr) noexcept
ll_tracy_delete(ptr);
}
-// C-style malloc/free can't be so easily overridden, so we define tracy versions and use
-// a pre-processor #define in linden_common.h to redirect to them. The parens around the native
-// functions below prevents recursive substitution by the preprocessor.
-//
-// Unaligned mallocs are rare in LL code but hooking them causes problems in 3p lib code (looking at
-// you, Havok), so we'll only capture the aligned version.
-
-void *tracy_aligned_malloc(size_t size, size_t alignment)
+void operator delete(void *ptr, std::align_val_t align) noexcept
{
- auto ptr = ll_aligned_malloc_fallback(size, alignment);
- if (ptr) LL_PROFILE_ALLOC(ptr, size);
- return ptr;
+ ll_tracy_aligned_delete(ptr);
}
-void tracy_aligned_free(void *memblock)
+void operator delete[](void* ptr, std::align_val_t align) noexcept
{
- LL_PROFILE_FREE(memblock);
- ll_aligned_free_fallback(memblock);
+ ll_tracy_aligned_delete(ptr);
}
-#endif
+#endif // TRACY_ENABLE && !LL_PROFILER_ENABLE_TRACY_OPENGL
//static
bool LLCommon::sAprInitialized = false;
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 3fe7e09e7e..9e95d9c85f 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -140,7 +140,7 @@ LLCoros::LLCoros():
// Previously we used
// boost::context::guarded_stack_allocator::default_stacksize();
// empirically this is insufficient.
- mStackSize(512*1024),
+ mStackSize(1024*1024),
// mCurrent does NOT own the current CoroData instance -- it simply
// points to it. So initialize it with a no-op deleter.
mCurrent{ [](CoroData*){} }
@@ -172,7 +172,7 @@ void LLCoros::cleanupSingleton()
// don't use llcoro::suspend() because that module depends
// on this one
// This will yield current(main) thread and will let active
- // coroutines run once
+ // corutines run once
boost::this_fiber::yield();
}
printActiveCoroutines("after pumping");
@@ -303,6 +303,75 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
return name;
}
+namespace
+{
+
+#if LL_WINDOWS
+
+static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
+{
+ if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
+ {
+ // Handled
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else
+ {
+ // handle it, convert to std::exception
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void cpphandle(const LLCoros::callable_t& callable, const std::string& name)
+{
+ // SE and C++ can not coexists, thus two handlers
+ try
+ {
+ callable();
+ }
+ catch (const LLCoros::Stop& exc)
+ {
+ LL_INFOS("LLCoros") << "coroutine " << name << " terminating because "
+ << exc.what() << LL_ENDL;
+ }
+ catch (const LLContinueError&)
+ {
+ // Any uncaught exception derived from LLContinueError will be caught
+ // here and logged. This coroutine will terminate but the rest of the
+ // viewer will carry on.
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
+ }
+}
+
+void sehandle(const LLCoros::callable_t& callable, const std::string& name)
+{
+ __try
+ {
+ // handle stop and continue exceptions first
+ cpphandle(callable, name);
+ }
+ __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // convert to C++ styled exception
+ // Note: it might be better to use _se_set_translator
+ // if you want exception to inherit full callstack
+ char integer_string[512];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+}
+#endif // LL_WINDOWS
+} // anonymous namespace
+
// Top-level wrapper around caller's coroutine callable.
// Normally we like to pass strings and such by const reference -- but in this
// case, we WANT to copy both the name and the callable to our local stack!
@@ -313,10 +382,14 @@ void LLCoros::toplevel(std::string name, callable_t callable)
// set it as current
mCurrent.reset(&corodata);
+#ifdef LL_WINDOWS
+ // can not use __try directly, toplevel requires unwinding, thus use of a wrapper
+ sehandle(callable, name);
+#else // LL_WINDOWS
// run the code the caller actually wants in the coroutine
try
{
- LL::seh::catcher(callable);
+ callable();
}
catch (const Stop& exc)
{
@@ -338,6 +411,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
<< name << LL_ENDL;
LLCoros::instance().saveException(name, std::current_exception());
}
+#endif // else LL_WINDOWS
}
//static
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index c3820ae987..9df52b6ed5 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -37,7 +37,7 @@
#include "mutex.h"
#include "llsingleton.h"
#include "llinstancetracker.h"
-#include <boost/function.hpp>
+#include <functional>
#include <string>
#include <exception>
#include <queue>
@@ -112,7 +112,7 @@ public:
/// stuck with the term "coroutine."
typedef boost::fibers::fiber coro;
/// Canonical callable type
- typedef boost::function<void()> callable_t;
+ typedef std::function<void()> callable_t;
/**
* Create and start running a new coroutine with specified name. The name
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp
index b38864688d..5205699b92 100644
--- a/indra/llcommon/lldate.cpp
+++ b/indra/llcommon/lldate.cpp
@@ -77,6 +77,15 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
}
+std::string LLDate::toLocalDateString (std::string fmt) const
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ time_t locSeconds = (time_t) mSecondsSinceEpoch;
+ struct tm * lt = localtime (&locSeconds);
+ return toHTTPDateString(lt, fmt);
+}
+
std::string LLDate::toHTTPDateString (std::string fmt) const
{
LL_PROFILE_ZONE_SCOPED;
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
index 1a69a04232..34c8692f20 100644
--- a/indra/llcommon/lldate.h
+++ b/indra/llcommon/lldate.h
@@ -77,6 +77,7 @@ public:
std::string asRFC1123() const;
void toStream(std::ostream&) const;
bool split(S32 *year, S32 *month = NULL, S32 *day = NULL, S32 *hour = NULL, S32 *min = NULL, S32 *sec = NULL) const;
+ std::string toLocalDateString(std::string fmt) const;
std::string toHTTPDateString (std::string fmt) const;
static std::string toHTTPDateString (tm * gmt, std::string fmt);
/**
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
index 3f10420d41..19d65b78b6 100644
--- a/indra/llcommon/lldeadmantimer.h
+++ b/indra/llcommon/lldeadmantimer.h
@@ -99,13 +99,10 @@ public:
/// during updates. If false, cpu usage data isn't
/// collected and will be zero if queried.
LLDeadmanTimer(F64 horizon, bool inc_cpu);
+ ~LLDeadmanTimer() = default;
- ~LLDeadmanTimer()
- {}
-
-private:
- LLDeadmanTimer(const LLDeadmanTimer &); // Not defined
- void operator=(const LLDeadmanTimer &); // Not defined
+ LLDeadmanTimer(const LLDeadmanTimer &) = delete;
+ LLDeadmanTimer& operator=(const LLDeadmanTimer&) = delete;
public:
/// Get the current time. Zero-basis for this time
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index 2fbb26dc1a..232987da14 100644
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -171,13 +171,13 @@ constexpr U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 +
// recursion tail
template <typename T>
-inline auto llmax(T data)
+constexpr auto llmax(T data)
{
return data;
}
template <typename T0, typename T1, typename... Ts>
-inline auto llmax(T0 d0, T1 d1, Ts... rest)
+constexpr auto llmax(T0 d0, T1 d1, Ts... rest)
{
auto maxrest = llmax(d1, rest...);
return (d0 > maxrest)? d0 : maxrest;
@@ -185,20 +185,20 @@ inline auto llmax(T0 d0, T1 d1, Ts... rest)
// recursion tail
template <typename T>
-inline auto llmin(T data)
+constexpr auto llmin(T data)
{
return data;
}
template <typename T0, typename T1, typename... Ts>
-inline auto llmin(T0 d0, T1 d1, Ts... rest)
+constexpr auto llmin(T0 d0, T1 d1, Ts... rest)
{
auto minrest = llmin(d1, rest...);
return (d0 < minrest) ? d0 : minrest;
}
template <typename A, typename MIN, typename MAX>
-inline A llclamp(A a, MIN minval, MAX maxval)
+constexpr A llclamp(A a, MIN minval, MAX maxval)
{
A aminval{ static_cast<A>(minval) }, amaxval{ static_cast<A>(maxval) };
if ( a < aminval )
@@ -213,13 +213,13 @@ inline A llclamp(A a, MIN minval, MAX maxval)
}
template <class LLDATATYPE>
-inline LLDATATYPE llclampf(LLDATATYPE a)
+constexpr LLDATATYPE llclampf(LLDATATYPE a)
{
return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(1));
}
template <class LLDATATYPE>
-inline LLDATATYPE llclampb(LLDATATYPE a)
+constexpr LLDATATYPE llclampb(LLDATATYPE a)
{
return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(255));
}
diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h
index 47b6fedc7d..a1b5c83caf 100644
--- a/indra/llcommon/lldependencies.h
+++ b/indra/llcommon/lldependencies.h
@@ -30,6 +30,7 @@
#if ! defined(LL_LLDEPENDENCIES_H)
#define LL_LLDEPENDENCIES_H
+#include <functional>
#include <string>
#include <vector>
#include <set>
@@ -38,7 +39,6 @@
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
-#include <boost/function.hpp>
#include <boost/bind.hpp>
#include "llexception.h"
@@ -217,7 +217,7 @@ class LLDependencies: public LLDependenciesBase
/// We have various ways to get the dependencies for a given DepNode.
/// Rather than having to restate each one for 'after' and 'before'
/// separately, pass a dep_selector so we can apply each to either.
- typedef boost::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
+ typedef std::function<const typename DepNode::dep_set&(const DepNode&)> dep_selector;
public:
LLDependencies() {}
@@ -340,7 +340,7 @@ private:
public:
/// iterator over value_type entries
- typedef boost::transform_iterator<boost::function<value_type(DepNodeMapEntry&)>,
+ typedef boost::transform_iterator<std::function<value_type(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> iterator;
/// range over value_type entries
typedef boost::iterator_range<iterator> range;
@@ -352,7 +352,7 @@ public:
}
/// iterator over const_value_type entries
- typedef boost::transform_iterator<boost::function<const_value_type(const DepNodeMapEntry&)>,
+ typedef boost::transform_iterator<std::function<const_value_type(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_iterator;
/// range over const_value_type entries
typedef boost::iterator_range<const_iterator> const_range;
@@ -364,7 +364,7 @@ public:
}
/// iterator over stored NODEs
- typedef boost::transform_iterator<boost::function<NODE&(DepNodeMapEntry&)>,
+ typedef boost::transform_iterator<std::function<NODE&(DepNodeMapEntry&)>,
typename DepNodeMap::iterator> node_iterator;
/// range over stored NODEs
typedef boost::iterator_range<node_iterator> node_range;
@@ -380,7 +380,7 @@ public:
}
/// const iterator over stored NODEs
- typedef boost::transform_iterator<boost::function<const NODE&(const DepNodeMapEntry&)>,
+ typedef boost::transform_iterator<std::function<const NODE&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_node_iterator;
/// const range over stored NODEs
typedef boost::iterator_range<const_node_iterator> const_node_range;
@@ -396,7 +396,7 @@ public:
}
/// const iterator over stored KEYs
- typedef boost::transform_iterator<boost::function<const KEY&(const DepNodeMapEntry&)>,
+ typedef boost::transform_iterator<std::function<const KEY&(const DepNodeMapEntry&)>,
typename DepNodeMap::const_iterator> const_key_iterator;
/// const range over stored KEYs
typedef boost::iterator_range<const_key_iterator> const_key_range;
diff --git a/indra/llcommon/lldoubledispatch.h b/indra/llcommon/lldoubledispatch.h
index 25039c3e9c..ad4dc57d58 100644
--- a/indra/llcommon/lldoubledispatch.h
+++ b/indra/llcommon/lldoubledispatch.h
@@ -30,9 +30,7 @@
#define LL_LLDOUBLEDISPATCH_H
#include <list>
-#include <boost/function.hpp>
-#include <boost/bind.hpp>
-#include <boost/ref.hpp>
+#include <functional>
/**
* This class supports function calls which are virtual on the dynamic type of
@@ -156,9 +154,9 @@ public:
insert(t1, t2, func);
if (symmetrical)
{
- // Use boost::bind() to construct a param-swapping thunk. Don't
+ // Use std::bind() to construct a param-swapping thunk. Don't
// forget to reverse the parameters too.
- insert(t2, t1, boost::bind(func, _2, _1));
+ insert(t2, t1, std::bind(func, std::placeholders::_2, std::placeholders::_1));
}
}
@@ -193,7 +191,7 @@ public:
insert(Type<Type1>(), Type<Type2>(), func, insertion);
if (symmetrical)
{
- insert(Type<Type2>(), Type<Type1>(), boost::bind(func, _2, _1), insertion);
+ insert(Type<Type2>(), Type<Type1>(), std::bind(func, std::placeholders::_2, std::placeholders::_1), insertion);
}
}
@@ -271,8 +269,8 @@ private:
typename DispatchTable::iterator find(const ParamBaseType& param1, const ParamBaseType& param2)
{
return std::find_if(mDispatch.begin(), mDispatch.end(),
- boost::bind(&EntryBase::matches, _1,
- boost::ref(param1), boost::ref(param2)));
+ std::bind(&EntryBase::matches, std::placeholders::_1,
+ std::ref(param1), std::ref(param2)));
}
/// Look up the first matching entry.
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index d834098994..b14464382b 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -79,7 +79,7 @@ namespace {
//
if (s.size())
{
- OutputDebugString(utf8str_to_utf16str(s).c_str());
+ OutputDebugString(ll_convert<std::wstring>(s).c_str());
OutputDebugString(TEXT("\n"));
}
}
@@ -527,8 +527,8 @@ namespace
mFileLevelMap(),
mTagLevelMap(),
mUniqueLogMessages(),
- mCrashFunction(NULL),
- mTimeFunction(NULL),
+ mCrashFunction(nullptr),
+ mTimeFunction(nullptr),
mRecorders(),
mShouldLogCallCounter(0)
{
@@ -1231,7 +1231,7 @@ namespace
std::ostringstream message_stream;
- if (r->wantsTime() && s->mTimeFunction != NULL)
+ if (r->wantsTime() && s->mTimeFunction != nullptr)
{
message_stream << s->mTimeFunction();
}
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index 0a7b3d2046..d254fa5407 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -31,7 +31,7 @@
#include "llerror.h"
#include "llpointer.h"
#include "llrefcount.h"
-#include "boost/function.hpp"
+#include <functional>
#include <string>
class LLSD;
@@ -92,7 +92,7 @@ namespace LLError
Control functions.
*/
- typedef boost::function<void(const std::string&)> FatalFunction;
+ typedef std::function<void(const std::string&)> FatalFunction;
LL_COMMON_API void setFatalFunction(const FatalFunction&);
// The fatal function will be called after an message of LEVEL_ERROR
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index e1fc4764f6..bb2cd4fb2e 100644
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -137,6 +137,18 @@ void llcoro::suspendUntilTimeout(float seconds)
suspendUntilEventOnWithTimeout(bogus, seconds, timedout);
}
+void llcoro::suspendUntilNextFrame()
+{
+ LLCoros::checkStop();
+ LLCoros::TempStatus st("waiting for next frame");
+
+ // Listen for the next event on the "mainloop" event pump.
+ // Once per frame we get mainloop.post(newFrame);
+ LLEventPumpOrPumpName mainloop_pump("mainloop");
+ // Wait for the next event (the event data is ignored).
+ suspendUntilEventOn(mainloop_pump);
+}
+
namespace
{
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index 492563bb98..25a8a98e36 100644
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -85,6 +85,11 @@ void suspend();
void suspendUntilTimeout(float seconds);
/**
+ * Yield control from a coroutine until the next mainloop's newFrame event.
+ */
+void suspendUntilNextFrame();
+
+/**
* Post specified LLSD event on the specified LLEventPump, then suspend for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index 4c3c0f3414..97a60e2829 100644
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -33,9 +33,7 @@
#define LL_LLEVENTDISPATCHER_H
#include <boost/fiber/fss.hpp>
-#include <boost/function_types/is_member_function_pointer.hpp>
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
-#include <boost/hof/is_invocable.hpp> // until C++17, when we get std::is_invocable
#include <boost/iterator/transform_iterator.hpp>
#include <functional> // std::function
#include <memory> // std::unique_ptr
@@ -99,7 +97,7 @@ public:
template <typename CALLABLE,
typename=typename std::enable_if<
- boost::hof::is_invocable<CALLABLE, LLSD>::value
+ std::is_invocable<CALLABLE, LLSD>::value
>::type>
void add(const std::string& name,
const std::string& desc,
@@ -295,9 +293,8 @@ public:
* converted to the corresponding parameter type using LLSDParam.
*/
template <typename CALLABLE,
- typename=typename std::enable_if<
- ! boost::hof::is_invocable<CALLABLE, LLSD>()
- >::type>
+ typename=typename std::enable_if_t<
+ ! std::is_invocable<CALLABLE, LLSD>()>>
void add(const std::string& name,
const std::string& desc,
CALLABLE&& f)
@@ -318,7 +315,7 @@ public:
*/
template<typename Method, typename InstanceGetter,
typename = typename std::enable_if<
- boost::function_types::is_member_function_pointer<Method>::value &&
+ std::is_member_function_pointer<Method>::value &&
! std::is_convertible<InstanceGetter, LLSD>::value
>::type>
void add(const std::string& name, const std::string& desc, Method f,
@@ -338,7 +335,7 @@ public:
template<typename Function,
typename = typename std::enable_if<
boost::function_types::is_nonmember_callable_builtin<Function>::value &&
- ! boost::hof::is_invocable<Function, LLSD>::value
+ ! std::is_invocable<Function, LLSD>::value
>::type>
void add(const std::string& name, const std::string& desc, Function f,
const LLSD& params, const LLSD& defaults=LLSD());
@@ -364,7 +361,7 @@ public:
*/
template<typename Method, typename InstanceGetter,
typename = typename std::enable_if<
- boost::function_types::is_member_function_pointer<Method>::value &&
+ std::is_member_function_pointer<Method>::value &&
! std::is_convertible<InstanceGetter, LLSD>::value
>::type>
void add(const std::string& name, const std::string& desc, Method f,
diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h
index d8c7e15a27..8b917c23be 100644
--- a/indra/llcommon/lleventfilter.h
+++ b/indra/llcommon/lleventfilter.h
@@ -33,7 +33,8 @@
#include "stdtypes.h"
#include "lltimer.h"
#include "llsdutil.h"
-#include <boost/function.hpp>
+
+#include <functional>
class LLEventTimer;
class LLDate;
@@ -92,8 +93,8 @@ public:
/// construct and connect
LLEventTimeoutBase(LLEventPump& source);
- /// Callable, can be constructed with boost::bind()
- typedef boost::function<void()> Action;
+ /// Callable, can be constructed with std::bind()
+ typedef std::function<void()> Action;
/**
* Start countdown timer for the specified number of @a seconds. Forward
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 3c6743eac9..9a5324b598 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -44,7 +44,6 @@
#include <cmath>
#include <cctype>
// external library headers
-#include <boost/range/iterator_range.hpp>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 4bf1fa07a2..18c05a0081 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -41,11 +41,7 @@
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
-#include <boost/utility.hpp> // noncopyable
#include <boost/optional/optional.hpp>
-#include <boost/visit_each.hpp>
-#include <boost/ref.hpp> // reference_wrapper
-#include <boost/type_traits/is_pointer.hpp>
#include <boost/static_assert.hpp>
#include "llsd.h"
#include "llsingleton.h"
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index 107fdc2b2d..c0154a569f 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -15,12 +15,7 @@
#include "llexception.h"
// STL headers
// std headers
-#include <iomanip>
-#include <sstream>
#include <typeinfo>
-#if LL_WINDOWS
-#include <excpt.h>
-#endif // LL_WINDOWS
// external library headers
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception/error_info.hpp>
@@ -34,6 +29,7 @@
// On Windows, header-only implementation causes macro collisions -- use
// prebuilt library
#define BOOST_STACKTRACE_LINK
+#include <excpt.h>
#endif // LL_WINDOWS
#include <boost/stacktrace.hpp>
@@ -98,47 +94,25 @@ void annotate_exception_(boost::exception& exc)
// For windows SEH exception handling we sometimes need a filter that will
// separate C++ exceptions from C SEH exceptions
-static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-static constexpr U32 STATUS_STACK_FULL = 0xC00000FD;
+static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code)
+U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
{
- // Sadly, despite its diagnostic importance, trying to capture a
- // stacktrace when the stack is already blown only terminates us faster.
- if (code == STATUS_STACK_FULL)
- {
- stacktrace = "(stack overflow, no traceback)";
- }
- else
- {
- stacktrace = to_string(boost::stacktrace::stacktrace());
- }
-}
+ const auto stack = to_string(boost::stacktrace::stacktrace());
+ LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code
+ << "\n Stack trace: \n"
+ << stack << LL_ENDL;
-U32 LL::seh::common_filter(U32 code, struct _EXCEPTION_POINTERS*)
-{
if (code == STATUS_MSC_EXCEPTION)
{
- // C++ exception, don't stop at this handler
+ // C++ exception, go on
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
- // This is a non-C++ exception, e.g. hardware check.
- // Pass control into the handler block.
+ // handle it
return EXCEPTION_EXECUTE_HANDLER;
}
}
-void LL::seh::rethrow(U32 code, const std::string& stacktrace)
-{
- std::ostringstream out;
- out << "Windows exception 0x" << std::hex << code;
- if (! stacktrace.empty())
- {
- out << '\n' << stacktrace;
- }
- LLTHROW(Windows_SEH_exception(out.str()));
-}
-
#endif //LL_WINDOWS
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
index f58a553eb3..68e609444e 100644
--- a/indra/llcommon/llexception.h
+++ b/indra/llcommon/llexception.h
@@ -12,7 +12,6 @@
#if ! defined(LL_LLEXCEPTION_H)
#define LL_LLEXCEPTION_H
-#include "always_return.h"
#include <stdexcept>
#include <boost/exception/exception.hpp>
#include <boost/throw_exception.hpp>
@@ -103,115 +102,14 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str
log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
void log_unhandled_exception_(const char*, int, const char*, const std::string&);
-/*****************************************************************************
-* Structured Exception Handling
-*****************************************************************************/
-// this is used in platform-generic code -- define outside #if LL_WINDOWS
-struct Windows_SEH_exception: public LLException
-{
- Windows_SEH_exception(const std::string& what): LLException(what) {}
-};
-
-namespace LL
-{
-namespace seh
-{
-
-#if LL_WINDOWS //-------------------------------------------------------------
-
-void fill_stacktrace(std::string& stacktrace, U32 code);
-
-// wrapper around caller's U32 filter(U32 code, struct _EXCEPTION_POINTERS*)
-// filter function: capture a stacktrace, if possible, before forwarding the
-// call to the caller's filter() function
-template <typename FILTER>
-U32 filter_(std::string& stacktrace, FILTER&& filter,
- U32 code, struct _EXCEPTION_POINTERS* exptrs)
-{
- // By the time the handler gets control, the stack has been unwound,
- // so report the stack trace now at filter() time.
- fill_stacktrace(stacktrace, code);
- return std::forward<FILTER>(filter)(code, exptrs);
-}
-
-template <typename TRYCODE, typename FILTER, typename HANDLER>
-auto catcher_inner(std::string& stacktrace,
- TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler)
-{
- __try
- {
- return std::forward<TRYCODE>(trycode)();
- }
- __except (filter_(stacktrace,
- std::forward<FILTER>(filter),
- GetExceptionCode(), GetExceptionInformation()))
- {
- return always_return<decltype(trycode())>(
- std::forward<HANDLER>(handler), GetExceptionCode(), stacktrace);
- }
-}
-
-// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*),
-// handler(U32, const std::string& stacktrace)
-// stacktrace may or may not be available
-template <typename TRYCODE, typename FILTER, typename HANDLER>
-auto catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler)
-{
- // Construct and destroy this stacktrace string in the outer function
- // because we can't do either in the function with __try/__except.
- std::string stacktrace;
- return catcher_inner(stacktrace,
- std::forward<TRYCODE>(trycode),
- std::forward<FILTER>(filter),
- std::forward<HANDLER>(handler));
-}
-// common_filter() handles the typical case in which we want our handler
-// clause to handle only Structured Exceptions rather than explicitly-thrown
-// C++ exceptions
-U32 common_filter(U32 code, struct _EXCEPTION_POINTERS*);
-
-// dyadic variant specifies try(), handler(U32, stacktrace), assumes common_filter()
-template <typename TRYCODE, typename HANDLER>
-auto catcher(TRYCODE&& trycode, HANDLER&& handler)
-{
- return catcher(std::forward<TRYCODE>(trycode),
- common_filter,
- std::forward<HANDLER>(handler));
-}
-
-// monadic variant specifies try(), assumes default filter and handler
-template <typename TRYCODE>
-auto catcher(TRYCODE&& trycode)
-{
- return catcher(std::forward<TRYCODE>(trycode), rethrow);
-}
-
-[[noreturn]] void rethrow(U32 code, const std::string& stacktrace);
-
-#else // not LL_WINDOWS -----------------------------------------------------
-
-template <typename TRYCODE, typename FILTER, typename HANDLER>
-auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&)
-{
- return std::forward<TRYCODE>(trycode)();
-}
-
-template <typename TRYCODE, typename HANDLER>
-auto catcher(TRYCODE&& trycode, HANDLER&&)
-{
- return std::forward<TRYCODE>(trycode)();
-}
-
-template <typename TRYCODE>
-auto catcher(TRYCODE&& trycode)
-{
- return std::forward<TRYCODE>(trycode)();
-}
+#if LL_WINDOWS
-#endif // not LL_WINDOWS -----------------------------------------------------
+// SEH exception filtering for use in __try __except
+// Separates C++ exceptions from C SEH exceptions
+// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&);
+U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop);
-} // namespace LL::seh
-} // namespace LL
+#endif //LL_WINDOWS
#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index 7ceb57336e..be0cab36e3 100644
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -64,7 +64,7 @@ bool BlockTimer::sLog = false;
std::string BlockTimer::sLogName = "";
bool BlockTimer::sMetricLog = false;
-#if LL_LINUX
+#if LL_LINUX || (LL_DARWIN && LL_ARM64)
U64 BlockTimer::sClockResolution = 1000000000; // Nanosecond resolution
#else
U64 BlockTimer::sClockResolution = 1000000; // Microsecond resolution
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index a69a03e419..fde168b022 100644
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -30,9 +30,14 @@
#include "llinstancetracker.h"
#include "lltrace.h"
#include "lltreeiterators.h"
+#include "llprocessor.h"
+#if LL_X86 || LL_X86_64
#if LL_WINDOWS
#include <intrin.h>
+#else
+#include <x86intrin.h>
+#endif
#endif
#define LL_FAST_TIMER_ON 1
@@ -68,38 +73,17 @@ public:
//
// Windows implementation of CPU clock
//
-
- //
- // NOTE: put back in when we aren't using platform sdk anymore
- //
- // because MS has different signatures for these functions in winnt.h
- // need to rename them to avoid conflicts
- //#define _interlockedbittestandset _renamed_interlockedbittestandset
- //#define _interlockedbittestandreset _renamed_interlockedbittestandreset
- //#include <intrin.h>
- //#undef _interlockedbittestandset
- //#undef _interlockedbittestandreset
-
- //inline U32 getCPUClockCount32()
- //{
- // U64 time_stamp = __rdtsc();
- // return (U32)(time_stamp >> 8);
- //}
- //
- //// return full timer value, *not* shifted by 8 bits
- //inline U64 getCPUClockCount64()
- //{
- // return __rdtsc();
- //}
-
-
+#if LL_FASTTIMER_USE_RDTSC
// shift off lower 8 bits for lower resolution but longer term timing
// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
-#if LL_FASTTIMER_USE_RDTSC
static U32 getCPUClockCount32()
{
+#if _M_ARM64
+ unsigned __int64 val = _ReadStatusReg(ARM64_PMCCNTR_EL0);
+#else
unsigned __int64 val = __rdtsc();
+#endif
val = val >> 8;
return static_cast<U32>(val);
}
@@ -107,7 +91,11 @@ public:
// return full timer value, *not* shifted by 8 bits
static U64 getCPUClockCount64()
{
+#if _M_ARM64
+ return static_cast<U64>( _ReadStatusReg(ARM64_PMCCNTR_EL0) );
+#else
return static_cast<U64>( __rdtsc() );
+#endif
}
#else
@@ -173,23 +161,39 @@ public:
#endif // (LL_LINUX) && !(defined(__i386__) || defined(__amd64__))
+/*
+#if LL_DARWIN && LL_ARM64
+ //
+ // Mac implementation of CPU clock - non-x86.
+ //
+ static U64 getCPUClockCount64()
+ {
+ return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
+ }
+
+ static U32 getCPUClockCount32()
+ {
+ return (U32)(getCPUClockCount64() >> 8);
+ }
+#endif // LL_DARWIN && LL_ARM64
+*/
+
#if (LL_LINUX || LL_DARWIN || __FreeBSD__) && (defined(__i386__) || defined(__amd64__))
//
// Mac+Linux FAST x86 implementation of CPU clock
+ //
+#if LL_FASTTIMER_USE_RDTSC
static U32 getCPUClockCount32()
{
- U32 low(0),high(0);
- __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) );
- return (low>>8) | (high<<24);
+ U64 time_stamp = __rdtsc() >> 8U;
+ return static_cast<U32>(time_stamp);
}
static U64 getCPUClockCount64()
{
- U32 low(0),high(0);
- __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) );
- return (U64)low | ( ((U64)high) << 32);
+ return static_cast<U64>(__rdtsc());
}
-
+#endif
#endif
static BlockTimerStatHandle& getRootTimeBlock();
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index d0bc8f7652..a539e4fe28 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -35,7 +35,6 @@
#if LL_WINDOWS
#include "llwin32headers.h"
-#include <stdlib.h> // Windows errno
#include <vector>
#else
#include <errno.h>
@@ -49,6 +48,86 @@ static std::string empty;
// variants of strerror() to report errors.
#if LL_WINDOWS
+// For the situations where we directly call into Windows API functions we need to translate
+// the Windows error codes into errno values
+namespace
+{
+ struct errentry
+ {
+ unsigned long oserr; // Windows OS error value
+ int errcode; // System V error code
+ };
+}
+
+// translation table between Windows OS error value and System V errno code
+static errentry const errtable[]
+{
+ { ERROR_INVALID_FUNCTION, EINVAL }, // 1
+ { ERROR_FILE_NOT_FOUND, ENOENT }, // 2
+ { ERROR_PATH_NOT_FOUND, ENOENT }, // 3
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4
+ { ERROR_ACCESS_DENIED, EACCES }, // 5
+ { ERROR_INVALID_HANDLE, EBADF }, // 6
+ { ERROR_ARENA_TRASHED, ENOMEM }, // 7
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8
+ { ERROR_INVALID_BLOCK, ENOMEM }, // 9
+ { ERROR_BAD_ENVIRONMENT, E2BIG }, // 10
+ { ERROR_BAD_FORMAT, ENOEXEC }, // 11
+ { ERROR_INVALID_ACCESS, EINVAL }, // 12
+ { ERROR_INVALID_DATA, EINVAL }, // 13
+ { ERROR_INVALID_DRIVE, ENOENT }, // 15
+ { ERROR_CURRENT_DIRECTORY, EACCES }, // 16
+ { ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
+ { ERROR_NO_MORE_FILES, ENOENT }, // 18
+ { ERROR_LOCK_VIOLATION, EACCES }, // 33
+ { ERROR_BAD_NETPATH, ENOENT }, // 53
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
+ { ERROR_BAD_NET_NAME, ENOENT }, // 67
+ { ERROR_FILE_EXISTS, EEXIST }, // 80
+ { ERROR_CANNOT_MAKE, EACCES }, // 82
+ { ERROR_FAIL_I24, EACCES }, // 83
+ { ERROR_INVALID_PARAMETER, EINVAL }, // 87
+ { ERROR_NO_PROC_SLOTS, EAGAIN }, // 89
+ { ERROR_DRIVE_LOCKED, EACCES }, // 108
+ { ERROR_BROKEN_PIPE, EPIPE }, // 109
+ { ERROR_DISK_FULL, ENOSPC }, // 112
+ { ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114
+ { ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130
+ { ERROR_NEGATIVE_SEEK, EINVAL }, // 131
+ { ERROR_SEEK_ON_DEVICE, EACCES }, // 132
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145
+ { ERROR_NOT_LOCKED, EACCES }, // 158
+ { ERROR_BAD_PATHNAME, ENOENT }, // 161
+ { ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164
+ { ERROR_LOCK_FAILED, EACCES }, // 167
+ { ERROR_ALREADY_EXISTS, EEXIST }, // 183
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215
+ { ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
+};
+
+static int set_errno_from_oserror(unsigned long oserr)
+{
+ if (!oserr)
+ return 0;
+
+ // Check the table for the Windows OS error code
+ for (const struct errentry &entry : errtable)
+ {
+ if (oserr == entry.oserr)
+ {
+ _set_errno(entry.errcode);
+ return -1;
+ }
+ }
+
+ _set_errno(EINVAL);
+ return -1;
+}
+
// On Windows, use strerror_s().
std::string strerr(int errn)
{
@@ -57,7 +136,67 @@ std::string strerr(int errn)
return buffer;
}
-typedef std::basic_ios<char,std::char_traits < char > > _Myios;
+inline bool is_slash(wchar_t const c)
+{
+ return c == L'\\' || c == L'/';
+}
+
+static std::wstring utf8path_to_wstring(const std::string& utf8path)
+{
+ if (utf8path.size() >= MAX_PATH)
+ {
+ // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
+ std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
+ // We need to make sure that the path does not contain forward slashes as above
+ // prefix does bypass the path normalization that replaces slashes with backslashes
+ // before passing the path to kernel mode APIs
+ std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
+ return utf16path;
+ }
+ return ll_convert<std::wstring>(utf8path);
+}
+
+static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
+{
+ unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (dontFollowSymLink)
+ {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+ HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, flags, nullptr);
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ FILE_ATTRIBUTE_TAG_INFO attribute_info;
+ if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
+ {
+ // A volume path alone (only drive letter) is not recognized as directory while it technically is
+ bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
+ (iswalpha(utf16path[0]) && utf16path[1] == ':' &&
+ (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
+ unsigned short st_mode = is_directory ? S_IFDIR :
+ (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
+ st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
+ // we do not try to guess executable flag
+
+ // propagate user bits to group/other fields:
+ st_mode |= (st_mode & 0700) >> 3;
+ st_mode |= (st_mode & 0700) >> 6;
+
+ CloseHandle(file_handle);
+ return st_mode;
+ }
+ }
+ // Retrieve last error and set errno before calling CloseHandle()
+ set_errno_from_oserror(GetLastError());
+
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(file_handle);
+ }
+ return 0;
+}
#else
// On Posix we want to call strerror_r(), but alarmingly, there are two
@@ -108,18 +247,13 @@ std::string strerr(int errn)
}
#endif // ! LL_WINDOWS
-// On either system, shorthand call just infers global 'errno'.
-std::string strerr()
-{
- return strerr(errno);
-}
-
-int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
+static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
{
if (rc < 0)
{
// Capture errno before we start emitting output
int errn = errno;
+
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
// EEXIST. Don't warn if caller explicitly says this errno is okay.
@@ -176,68 +310,59 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
+ // We often use mkdir() to ensure the existence of a directory that might
+ // already exist. There is no known case in which we want to call out as
+ // an error the requested directory already existing.
#if LL_WINDOWS
// permissions are ignored on Windows
- std::string utf8dirname = dirname;
- llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
- int rc = _wmkdir(utf16dirname.c_str());
+ int rc = 0;
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
+ if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
+ {
+ // Only treat other errors than an already existing file as a real error
+ unsigned long oserr = GetLastError();
+ if (oserr != ERROR_ALREADY_EXISTS)
+ {
+ rc = set_errno_from_oserror(oserr);
+ }
+ }
#else
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
-#endif
- // We often use mkdir() to ensure the existence of a directory that might
- // already exist. There is no known case in which we want to call out as
- // an error the requested directory already existing.
if (rc < 0 && errno == EEXIST)
{
// this is not the error you want, move along
return 0;
}
+#endif
// anything else might be a problem
- return warnif("mkdir", dirname, rc, EEXIST);
+ return warnif("mkdir", dirname, rc);
}
// static
-int LLFile::rmdir(const std::string& dirname)
+int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
#if LL_WINDOWS
- // permissions are ignored on Windows
- std::string utf8dirname = dirname;
- llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
int rc = _wrmdir(utf16dirname.c_str());
#else
int rc = ::rmdir(dirname.c_str());
#endif
- return warnif("rmdir", dirname, rc);
+ return warnif("rmdir", dirname, rc, suppress_error);
}
// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */
+LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
{
#if LL_WINDOWS
- std::string utf8filename = filename;
- std::string utf8mode = std::string(mode);
- llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
- return _wfopen(utf16filename.c_str(),utf16mode.c_str());
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
+ return _wfopen(utf16filename.c_str(), utf16mode.c_str());
#else
- return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */
-#endif
-}
-
-LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag)
-{
-#if LL_WINDOWS
- std::string utf8filename = filename;
- std::string utf8mode = std::string(mode);
- llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
- return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
-#else
- llassert(0);//No corresponding function on non-windows
- return NULL;
+ return ::fopen(filename.c_str(),mode);
#endif
}
+// static
int LLFile::close(LLFILE * file)
{
int ret_value = 0;
@@ -248,9 +373,10 @@ int LLFile::close(LLFILE * file)
return ret_value;
}
+// static
std::string LLFile::getContents(const std::string& filename)
{
- LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */
+ LLFILE* fp = LLFile::fopen(filename, "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
@@ -267,45 +393,80 @@ std::string LLFile::getContents(const std::string& filename)
return LLStringUtil::null;
}
-int LLFile::remove(const std::string& filename, int supress_error)
+// static
+int LLFile::remove(const std::string& filename, int suppress_error)
{
#if LL_WINDOWS
- std::string utf8filename = filename;
- llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- int rc = _wremove(utf16filename.c_str());
+ // Posix remove() works on both files and directories although on Windows
+ // remove() and its wide char variant _wremove() only removes files just
+ // as its siblings unlink() and _wunlink().
+ // If we really only want to support files we should instead use
+ // unlink() in the non-Windows part below too
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename);
+ if (S_ISDIR(st_mode))
+ {
+ rc = _wrmdir(utf16filename.c_str());
+ }
+ else if (S_ISREG(st_mode))
+ {
+ rc = _wunlink(utf16filename.c_str());
+ }
+ else if (st_mode)
+ {
+ // it is something else than a file or directory
+ // this should not really happen as long as we do not allow for symlink
+ // detection in the optional parameter to get_fileattr()
+ rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ // get_fileattr() failed and already set errno, preserve it for correct error reporting
+ }
#else
int rc = ::remove(filename.c_str());
#endif
- return warnif("remove", filename, rc, supress_error);
+ return warnif("remove", filename, rc, suppress_error);
}
-int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error)
+// static
+int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
{
#if LL_WINDOWS
- std::string utf8filename = filename;
- std::string utf8newname = newname;
- llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
- int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
+ // Posix rename() will gladly overwrite a file at newname if it exists, the Windows
+ // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
+ // API MoveFileEx() and use its flags to specify that overwrite is allowed.
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ std::wstring utf16newname = utf8path_to_wstring(newname);
+ int rc = 0;
+ if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ {
+ rc = set_errno_from_oserror(GetLastError());
+ }
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
- return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error);
+ return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
+// Make this a define rather than using magic numbers multiple times in the code
+#define LLFILE_COPY_BUFFER_SIZE 16384
+
+// static
bool LLFile::copy(const std::string& from, const std::string& to)
{
bool copied = false;
- LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
+ LLFILE* in = LLFile::fopen(from, "rb");
if (in)
{
- LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
+ LLFILE* out = LLFile::fopen(to, "wb");
if (out)
{
- char buf[16384]; /* Flawfinder: ignore */
+ char buf[LLFILE_COPY_BUFFER_SIZE];
size_t readbytes;
bool write_ok = true;
- while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
+ while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
{
if (fwrite(buf, 1, readbytes, out) != readbytes)
{
@@ -324,34 +485,62 @@ bool LLFile::copy(const std::string& from, const std::string& to)
return copied;
}
-int LLFile::stat(const std::string& filename, llstat* filestatus)
+// static
+int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
{
#if LL_WINDOWS
- std::string utf8filename = filename;
- llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- int rc = _wstat(utf16filename.c_str(),filestatus);
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ int rc = _wstat64(utf16filename.c_str(), filestatus);
#else
- int rc = ::stat(filename.c_str(),filestatus);
+ int rc = ::stat(filename.c_str(), filestatus);
#endif
- // We use stat() to determine existence (see isfile(), isdir()).
- // Don't spam the log if the subject pathname doesn't exist.
- return warnif("stat", filename, rc, ENOENT);
+ return warnif("stat", filename, rc, suppress_error);
}
-bool LLFile::isdir(const std::string& filename)
+// static
+unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
{
- llstat st;
+#if LL_WINDOWS
+ // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
+ // to just get the attributes
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
+ if (st_mode)
+ {
+ return st_mode;
+ }
+#else
+ llstat filestatus;
+ int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
+ if (rc == 0)
+ {
+ return filestatus.st_mode;
+ }
+#endif
+ warnif("getattr", filename, rc, suppress_error);
+ return 0;
+}
- return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
+// static
+bool LLFile::isdir(const std::string& filename)
+{
+ return S_ISDIR(getattr(filename));
}
+// static
bool LLFile::isfile(const std::string& filename)
{
- llstat st;
+ return S_ISREG(getattr(filename));
+}
- return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
+// static
+bool LLFile::islink(const std::string& filename)
+{
+ return S_ISLNK(getattr(filename, true));
}
+// static
const char *LLFile::tmpdir()
{
static std::string utf8path;
@@ -378,89 +567,22 @@ const char *LLFile::tmpdir()
return utf8path.c_str();
}
-
-/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
-
#if LL_WINDOWS
-LLFILE * LLFile::_Fiopen(const std::string& filename,
- std::ios::openmode mode)
-{ // open a file
- static const char *mods[] =
- { // fopen mode strings corresponding to valid[i]
- "r", "w", "w", "a", "rb", "wb", "wb", "ab",
- "r+", "w+", "a+", "r+b", "w+b", "a+b",
- 0};
- static const int valid[] =
- { // valid combinations of open flags
- ios_base::in,
- ios_base::out,
- ios_base::out | ios_base::trunc,
- ios_base::out | ios_base::app,
- ios_base::in | ios_base::binary,
- ios_base::out | ios_base::binary,
- ios_base::out | ios_base::trunc | ios_base::binary,
- ios_base::out | ios_base::app | ios_base::binary,
- ios_base::in | ios_base::out,
- ios_base::in | ios_base::out | ios_base::trunc,
- ios_base::in | ios_base::out | ios_base::app,
- ios_base::in | ios_base::out | ios_base::binary,
- ios_base::in | ios_base::out | ios_base::trunc
- | ios_base::binary,
- ios_base::in | ios_base::out | ios_base::app
- | ios_base::binary,
- 0};
-
- LLFILE *fp = 0;
- int n;
- ios_base::openmode atendflag = mode & ios_base::ate;
- ios_base::openmode norepflag = mode & ios_base::_Noreplace;
-
- if (mode & ios_base::_Nocreate)
- mode |= ios_base::in; // file must exist
- mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
- for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
- ; // look for a valid mode
-
- if (valid[n] == 0)
- return (0); // no valid mode
- else if (norepflag && mode & (ios_base::out | ios_base::app)
- && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
- { // file must not exist, close and fail
- fclose(fp);
- return (0);
- }
- else if (fp != 0 && fclose(fp) != 0)
- return (0); // can't close after test open
-// should open with protection here, if other than default
- else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
- return (0); // open failed
-
- if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
- return (fp); // no need to seek to end, or seek succeeded
-
- fclose(fp); // can't position at end
- return (0);
-}
-
-#endif /* LL_WINDOWS */
-
-
-#if LL_WINDOWS
/************** input file stream ********************************/
llifstream::llifstream() {}
// explicit
llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
- std::ifstream(utf8str_to_utf16str( _Filename ).c_str(),
+ std::ifstream(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::in)
{
}
void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
- std::ifstream::open(utf8str_to_utf16str(_Filename).c_str(),
+ std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
_Mode | ios_base::in);
}
@@ -472,14 +594,14 @@ llofstream::llofstream() {}
// explicit
llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
- std::ofstream(utf8str_to_utf16str( _Filename ).c_str(),
+ std::ofstream(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::out)
{
}
void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
- std::ofstream::open(utf8str_to_utf16str( _Filename ).c_str(),
+ std::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
_Mode | ios_base::out);
}
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 1661cbeb55..04a2946ac4 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -41,8 +41,9 @@ typedef FILE LLFILE;
#include <sys/stat.h>
#if LL_WINDOWS
-// windows version of stat function and stat data structure are called _stat
-typedef struct _stat llstat;
+// The Windows version of stat function and stat data structure are called _stat64
+// We use _stat64 here to support 64-bit st_size and time_t values
+typedef struct _stat64 llstat;
#else
typedef struct stat llstat;
#include <sys/types.h>
@@ -56,35 +57,110 @@ typedef struct stat llstat;
# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
+// Windows C runtime library does not define this and does not support symlink detection in the
+// stat functions but we do in our getattr() function
+#ifndef S_IFLNK
+#define S_IFLNK 0xA000 /* symlink */
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#endif
+
#include "llstring.h" // safe char* -> std::string conversion
+/// LLFile is a class of static functions operating on paths
+/// All the functions with a path string input take UTF8 path/filenames
class LL_COMMON_API LLFile
{
public:
- // All these functions take UTF8 path/filenames.
- static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
- static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
+ /// open a file with the specified access mode
+ static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
+ ///< 'accessmode' follows the rules of the Posix fopen() mode parameter
+ /// "r" open the file for reading only and positions the stream at the beginning
+ /// "r+" open the file for reading and writing and positions the stream at the beginning
+ /// "w" open the file for reading and writing and truncate it to zero length
+ /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
+ /// "a" open the file for reading and writing and position the stream at the end of the file
+ /// "a+" open or create the file for reading and writing and position the stream at the end of the file
+ ///
+ /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
+ /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
+ /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
+ /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
+ /// This means that it is always a good idea to append "b" specifically for binary file access to
+ /// avoid corruption of the binary consistency of the data stream when reading or writing
+ /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
+ /// @returns a valid LLFILE* pointer on success or NULL on failure
static int close(LLFILE * file);
+ /// retrieve the content of a file into a string
static std::string getContents(const std::string& filename);
+ ///< @returns the content of the file or an empty string on failure
- // perms is a permissions mask like 0777 or 0700. In most cases it will
- // be overridden by the user's umask. It is ignored on Windows.
- // mkdir() considers "directory already exists" to be SUCCESS.
+ /// create a directory
static int mkdir(const std::string& filename, int perms = 0700);
+ ///< perms is a permissions mask like 0777 or 0700. In most cases it will be
+ /// overridden by the user's umask. It is ignored on Windows.
+ /// mkdir() considers "directory already exists" to be not an error.
+ /// @returns 0 on success and -1 on failure.
+
+ //// remove a directory
+ static int rmdir(const std::string& filename, int suppress_error = 0);
+ ///< pass ENOENT in the optional 'suppress_error' parameter
+ /// if you don't want a warning in the log when the directory does not exist
+ /// @returns 0 on success and -1 on failure.
+
+ /// remove a file or directory
+ static int remove(const std::string& filename, int suppress_error = 0);
+ ///< pass ENOENT in the optional 'suppress_error' parameter
+ /// if you don't want a warning in the log when the directory does not exist
+ /// @returns 0 on success and -1 on failure.
- static int rmdir(const std::string& filename);
- static int remove(const std::string& filename, int supress_error = 0);
- static int rename(const std::string& filename,const std::string& newname, int supress_error = 0);
+ /// rename a file
+ static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
+ ///< it will silently overwrite newname if it exists without returning an error
+ /// Posix guarantees that if newname already exists, then there will be no moment
+ /// in which for other processes newname does not exist. There is no such guarantee
+ /// under Windows at this time. It may do it in the same way but the used Windows API
+ /// does not make such guarantees.
+ /// @returns 0 on success and -1 on failure.
+
+
+ /// copy the contents of file from 'from' to 'to' filename
static bool copy(const std::string& from, const std::string& to);
+ ///< @returns true on success and false on failure.
+
+ /// return the file stat structure for filename
+ static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
+ ///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
+ /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
+ /// is used to detect if a file exists
+ /// @returns 0 on success and -1 on failure.
+
+ /// get the file or directory attributes for filename
+ static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
+ ///< a more lightweight function on Windows to stat, that just returns the file attribute flags
+ /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
+ /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
+ /// warnings when the file or directory does not exist
+ /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
+ /// together with the three access bits which under Windows only the write bit is relevant.
+
+ /// check if filename is an existing directory
+ static bool isdir(const std::string& filename);
+ ///< @returns true if the path is for an existing directory
+
+ /// check if filename is an existing file
+ static bool isfile(const std::string& filename);
+ ///< @returns true if the path is for an existing file
- static int stat(const std::string& filename,llstat* file_status);
- static bool isdir(const std::string& filename);
- static bool isfile(const std::string& filename);
- static LLFILE * _Fiopen(const std::string& filename,
- std::ios::openmode mode);
+ /// check if filename is a symlink
+ static bool islink(const std::string& filename);
+ ///< @returns true if the path is pointing at a symlink
+ /// return a path to the temporary directory on the system
static const char * tmpdir();
};
diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h
index ceea1d9c48..fd7d32e79a 100644
--- a/indra/llcommon/llhandle.h
+++ b/indra/llcommon/llhandle.h
@@ -31,8 +31,6 @@
#include "llrefcount.h"
#include "llexception.h"
#include <stdexcept>
-#include <boost/type_traits/is_convertible.hpp>
-#include <boost/utility/enable_if.hpp>
#include <boost/throw_exception.hpp>
/**
@@ -90,7 +88,7 @@ public:
LLHandle() : mTombStone(getDefaultTombStone()) {}
template<typename U>
- LLHandle(const LLHandle<U>& other, typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0)
+ LLHandle(const LLHandle<U>& other, typename std::enable_if_t<std::is_convertible_v<U*, T*>>* dummy = 0)
: mTombStone(other.mTombStone)
{}
@@ -199,7 +197,7 @@ public:
}
template <typename U>
- LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const
+ LLHandle<U> getDerivedHandle(typename std::enable_if_t<std::is_convertible_v<U*, T*> >* dummy = 0) const
{
LLHandle<U> downcast_handle;
downcast_handle.mTombStone = getHandle().mTombStone;
diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h
index 2354c9f2ed..7cc9c6b930 100644
--- a/indra/llcommon/llinitdestroyclass.h
+++ b/indra/llcommon/llinitdestroyclass.h
@@ -37,7 +37,7 @@
#define LL_LLINITDESTROYCLASS_H
#include "llsingleton.h"
-#include <boost/function.hpp>
+#include <functional>
#include <typeinfo>
#include <vector>
#include <utility> // std::pair
@@ -50,7 +50,7 @@
class LLCallbackRegistry
{
public:
- typedef boost::function<void()> func_t;
+ typedef std::function<void()> func_t;
void registerCallback(const std::string& name, const func_t& func)
{
diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h
index 32d7b17034..b01ea0bfb1 100644
--- a/indra/llcommon/llinitparam.h
+++ b/indra/llcommon/llinitparam.h
@@ -28,11 +28,12 @@
#ifndef LL_LLPARAM_H
#define LL_LLPARAM_H
+#include <functional>
+#include <type_traits>
#include <vector>
#include <list>
+#include <unordered_map>
#include <boost/function.hpp>
-#include <boost/type_traits/is_convertible.hpp>
-#include <boost/type_traits/is_enum.hpp>
#include <boost/unordered_map.hpp>
#include "llerror.h"
@@ -105,6 +106,26 @@ namespace LLTypeTags
};
}
+namespace ll
+{
+ // Primary template: general case is false
+ template<typename T>
+ struct is_std_function : std::false_type
+ {
+ };
+
+ // Specialization for std::function
+ // R is the return type, Args is a parameter pack for argument types
+ template<typename R, typename... Args>
+ struct is_std_function<std::function<R(Args...)>> : std::true_type
+ {
+ };
+
+ // Helper variable template for convenience (C++14 onwards)
+ template<typename T>
+ constexpr bool is_std_function_v = is_std_function<T>::value;
+}
+
namespace LLInitParam
{
// used to indicate no matching value to a given name when parsing
@@ -114,7 +135,7 @@ namespace LLInitParam
// wraps comparison operator between any 2 values of the same type
// specialize to handle cases where equality isn't defined well, or at all
- template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value >
+ template <typename T, bool IS_BOOST_FUNCTION = std::is_constructible_v<T, boost::function_base> || ll::is_std_function_v<T>>
struct ParamCompare
{
static bool equals(const T &a, const T &b)
@@ -123,7 +144,7 @@ namespace LLInitParam
}
};
- // boost function types are not comparable
+ // boost and std function types are not comparable
template<typename T>
struct ParamCompare<T, true>
{
@@ -474,7 +495,7 @@ namespace LLInitParam
typedef bool (*parser_read_func_t)(Parser& parser, void* output);
typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
- typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
+ typedef std::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t;
typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t;
@@ -491,7 +512,7 @@ namespace LLInitParam
virtual ~Parser();
- template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0)
+ template <typename T> bool readValue(T& param, typename std::enable_if_t<!std::is_enum_v<T>>* dummy = 0)
{
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
if (found_it != mParserReadFuncs->end())
@@ -502,7 +523,7 @@ namespace LLInitParam
return false;
}
- template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0)
+ template <typename T> bool readValue(T& param, typename std::enable_if_t<std::is_enum_v<T> >* dummy = 0)
{
parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
if (found_it != mParserReadFuncs->end())
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 662a2511cd..1614cc6e57 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -61,7 +61,7 @@ public:
// Pass it a callback to our connect() method, so it can send events
// from a particular LLEventPump to the plugin without having to know
// this class or method name.
- mListener(new LLLeapListener(
+ mListener(std::make_unique<LLLeapListener>(
[this](LLEventPump& pump, const std::string& listener)
{ return connect(pump, listener); }))
{
@@ -188,6 +188,17 @@ public:
<< childout.peek(0, peeklen) << "..." << LL_ENDL;
}
+ // Handle any remaining stderr data (partial lines) the same way as we do
+ // for stdout: log it.
+ LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
+ if (childerr.size())
+ {
+ LLProcess::ReadPipe::size_type
+ peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childerr.size()));
+ LL_WARNS("LLLeap") << "Final stderr " << childerr.size() << " bytes: "
+ << childerr.peek(0, peeklen) << "..." << LL_ENDL;
+ }
+
// Kill this instance. MUST BE LAST before return!
delete this;
return false;
diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h
index cad4543d02..f5587d1d68 100644
--- a/indra/llcommon/llleaplistener.h
+++ b/indra/llcommon/llleaplistener.h
@@ -13,10 +13,9 @@
#define LL_LLLEAPLISTENER_H
#include "lleventapi.h"
+#include <functional>
#include <map>
#include <string>
-#include <boost/function.hpp>
-#include <boost/ptr_container/ptr_map.hpp>
/// Listener class implementing LLLeap query/control operations.
/// See https://jira.lindenlab.com/jira/browse/DEV-31978.
@@ -31,7 +30,7 @@ public:
* define the signature for a function that will perform that, and make
* our constructor accept such a function.
*/
- typedef boost::function<LLBoundListener(LLEventPump&, const std::string& listener)>
+ typedef std::function<LLBoundListener(LLEventPump&, const std::string& listener)>
ConnectFunc;
LLLeapListener(const ConnectFunc& connect);
~LLLeapListener();
diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp
index e999b8f597..c8ca586e7f 100644
--- a/indra/llcommon/llmd5.cpp
+++ b/indra/llcommon/llmd5.cpp
@@ -255,6 +255,11 @@ void LLMD5::raw_digest(unsigned char* s) const
memcpy(s, digest, 16); /* Flawfinder: ignore */
}
+#if LL_DARWIN
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
void LLMD5::hex_digest(char* s) const
{
if (!finalized)
@@ -273,6 +278,10 @@ void LLMD5::hex_digest(char* s) const
s[32] = '\0';
}
+#if LL_DARWIN
+#pragma clang diagnostic pop
+#endif
+
std::ostream& operator<<(std::ostream& stream, const LLMD5& context)
{
char s[33]; /* Flawfinder: ignore */
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 4a9359f8e2..c06ae93766 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -71,7 +71,7 @@ LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
#define ll_assert_aligned(ptr,alignment)
#endif
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || _M_X64
#include <xmmintrin.h>
#else
#include <sse2neon.h>
@@ -235,8 +235,6 @@ inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed wi
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
#if defined(LL_WINDOWS)
void* ret = _aligned_malloc(size, 32);
-#elif defined(LL_DARWIN)
- void* ret = ll_aligned_malloc_fallback( size, 32 );
#else
void *ret;
if (0 != posix_memalign(&ret, 32, size))
@@ -252,8 +250,31 @@ inline void ll_aligned_free_32(void *p)
LL_PROFILE_FREE(p);
#if defined(LL_WINDOWS)
_aligned_free(p);
-#elif defined(LL_DARWIN)
- ll_aligned_free_fallback( p );
+#else
+ free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+
+inline void* ll_aligned_malloc_64(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+#if defined(LL_WINDOWS)
+ void* ret = _aligned_malloc(size, 64);
+#else
+ void *ret;
+ if (0 != posix_memalign(&ret, 64, size))
+ return nullptr;
+#endif
+ LL_PROFILE_ALLOC(ret, size);
+ return ret;
+}
+
+inline void ll_aligned_free_64(void *p)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ LL_PROFILE_FREE(p);
+#if defined(LL_WINDOWS)
+ _aligned_free(p);
#else
free(p); // posix_memalign() is compatible with heap deallocator
#endif
@@ -265,19 +286,23 @@ LL_FORCE_INLINE void* ll_aligned_malloc(size_t size)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
void* ret;
- if (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0)
+ if constexpr (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0)
{
ret = malloc(size);
LL_PROFILE_ALLOC(ret, size);
}
- else if (ALIGNMENT == 16)
+ else if constexpr (ALIGNMENT == 16)
{
ret = ll_aligned_malloc_16(size);
}
- else if (ALIGNMENT == 32)
+ else if constexpr (ALIGNMENT == 32)
{
ret = ll_aligned_malloc_32(size);
}
+ else if constexpr (ALIGNMENT == 64)
+ {
+ ret = ll_aligned_malloc_64(size);
+ }
else
{
ret = ll_aligned_malloc_fallback(size, ALIGNMENT);
@@ -289,16 +314,20 @@ template<size_t ALIGNMENT>
LL_FORCE_INLINE void ll_aligned_free(void* ptr)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
- if (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN)
+ if constexpr (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN)
{
LL_PROFILE_FREE(ptr);
free(ptr);
}
- else if (ALIGNMENT == 16)
+ else if constexpr (ALIGNMENT == 16)
{
ll_aligned_free_16(ptr);
}
- else if (ALIGNMENT == 32)
+ else if constexpr (ALIGNMENT == 32)
+ {
+ return ll_aligned_free_32(ptr);
+ }
+ else if constexpr (ALIGNMENT == 64)
{
return ll_aligned_free_32(ptr);
}
@@ -314,6 +343,9 @@ LL_FORCE_INLINE void ll_aligned_free(void* ptr)
inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+#if defined(LL_ARM64)
+ memcpy(dst, src, bytes);
+#else
assert(src != NULL);
assert(dst != NULL);
assert(bytes > 0);
@@ -379,6 +411,7 @@ inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __
dst += 16;
src += 16;
}
+#endif
}
#ifndef __DEBUG_PRIVATE_MEM__
diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h
index 62943845a5..f3615a1270 100644
--- a/indra/llcommon/llmutex.h
+++ b/indra/llcommon/llmutex.h
@@ -29,7 +29,6 @@
#include "stdtypes.h"
#include "llthread.h"
-#include <boost/noncopyable.hpp>
#include "mutex.h"
#include <shared_mutex>
@@ -249,7 +248,7 @@ private:
* The constructor handles the lock, and the destructor handles
* the unlock. Instances of this class are <b>not</b> thread safe.
*/
-class LL_COMMON_API LLScopedLock : private boost::noncopyable
+class LL_COMMON_API LLScopedLock
{
public:
/**
@@ -265,6 +264,12 @@ public:
*/
~LLScopedLock();
+ /*
+ * @brief Non-copyable constructor and operator
+ */
+ LLScopedLock(const LLScopedLock&) = delete;
+ LLScopedLock& operator=(const LLScopedLock&) = delete;
+
/**
* @brief Check lock.
*/
diff --git a/indra/llcommon/llpounceable.h b/indra/llcommon/llpounceable.h
index 0421ce966a..e86098f20b 100644
--- a/indra/llcommon/llpounceable.h
+++ b/indra/llcommon/llpounceable.h
@@ -36,13 +36,13 @@
#define LL_LLPOUNCEABLE_H
#include "llsingleton.h"
-#include <boost/noncopyable.hpp>
#include <boost/call_traits.hpp>
-#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/value_init.hpp>
#include <boost/unordered_map.hpp>
#include <boost/signals2/signal.hpp>
+#include <type_traits>
+
// Forward declare the user template, since we want to be able to point to it
// in some of its implementation classes.
template <typename T, class TAG>
@@ -139,7 +139,7 @@ private:
// LLPounceable<T> is for an LLPounceable instance on the heap or the stack.
// LLPounceable<T, LLPounceableStatic> is for a static LLPounceable instance.
template <typename T, class TAG=LLPounceableQueue>
-class LLPounceable: public boost::noncopyable
+class LLPounceable
{
private:
typedef LLPounceableTraits<T, TAG> traits;
@@ -158,9 +158,13 @@ public:
mEmpty(empty)
{}
+ // Non-copyable
+ LLPounceable(const LLPounceable&) = delete;
+ LLPounceable& operator=(const LLPounceable&) = delete;
+
// make read access to mHeld as cheap and transparent as possible
operator T () const { return mHeld; }
- typename boost::remove_pointer<T>::type operator*() const { return *mHeld; }
+ typename std::remove_pointer<T>::type operator*() const { return *mHeld; }
typename boost::call_traits<T>::value_type operator->() const { return mHeld; }
// uncomment 'explicit' as soon as we allow C++11 compilation
/*explicit*/ operator bool() const { return bool(mHeld); }
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index e9f07f6fdf..7e2d0b47f7 100644
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -78,6 +78,23 @@
#endif
#endif
+// Set up CPU architecture defines
+#if LL_MSVC && defined(_M_ARM64)
+# define LL_ARM64 1
+#elif LL_GNUC && (defined(__arm64__) || defined(__aarch64__))
+# define LL_ARM64 1
+#elif LL_MSVC && _M_X64
+# define LL_X86_64 1
+# define LL_X86 1
+#elif LL_MSVC && _M_IX86
+# define LL_X86 1
+#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
+# define LL_X86_64 1
+# define LL_X86 1
+#elif LL_GNUC && ( defined(__i386__) )
+# define LL_X86 1
+#endif
+
// Deal with minor differences on Unixy OSes.
#if LL_DARWIN || LL_LINUX || __FreeBSD__
// Different name, same functionality.
@@ -120,11 +137,8 @@
#if LL_WINDOWS
#define LL_DLLEXPORT __declspec(dllexport)
#define LL_DLLIMPORT __declspec(dllimport)
-#elif LL_LINUX
-#define LL_DLLEXPORT __attribute__ ((visibility("default")))
-#define LL_DLLIMPORT
#else
-#define LL_DLLEXPORT
+#define LL_DLLEXPORT __attribute__ ((visibility("default")))
#define LL_DLLIMPORT
#endif // LL_WINDOWS
@@ -169,7 +183,7 @@
#define LL_TO_STRING_HELPER(x) #x
#define LL_TO_STRING(x) LL_TO_STRING_HELPER(x)
-#define LL_TO_WSTRING_HELPER(x) L#x
+#define LL_TO_WSTRING_HELPER(x) L## #x
#define LL_TO_WSTRING(x) LL_TO_WSTRING_HELPER(x)
#define LL_FILE_LINENO_MSG(msg) __FILE__ "(" LL_TO_STRING(__LINE__) ") : " msg
#define LL_GLUE_IMPL(x, y) x##y
@@ -189,4 +203,18 @@
#define LL_PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif
+#ifndef _M_ARM64
+#if LL_ARM64
+#define GLM_FORCE_NEON 1
+#else
+#define GLM_FORCE_SSE2 1
+#endif
+#endif
+
+#if LL_ARM64
+#define KDU_NEON_INTRINSICS 1
+#else
+#define KDU_X86_INTRINSICS 1
+#endif
+
#endif // not LL_LINDEN_PREPROCESSOR_H
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
index 2800cc5608..3120fa0100 100644
--- a/indra/llcommon/llprocess.cpp
+++ b/indra/llcommon/llprocess.cpp
@@ -457,7 +457,8 @@ public:
("slot", LLSD::Integer(mIndex))
("name", whichfile(mIndex))
("desc", mDesc)
- ("eof", state == CLOSED));
+ ("eof", state == CLOSED)
+ ("exhst", state == EXHAUSTED));
}
return false;
@@ -528,18 +529,9 @@ LLProcess::LLProcess(const LLSDOrParams& params):
// preserve existing semantics, we promise that mAttached defaults to the
// same setting as mAutokill.
mAttached(params.attached.isProvided()? params.attached : params.autokill),
- mPool(NULL),
- mPipes(NSLOTS)
+ mPool(NULL)
{
- // Hmm, when you construct a ptr_vector with a size, it merely reserves
- // space, it doesn't actually make it that big. Explicitly make it bigger.
- // Because of ptr_vector's odd semantics, have to push_back(0) the right
- // number of times! resize() wants to default-construct new BasePipe
- // instances, which fails because it's pure virtual. But because of the
- // constructor call, these push_back() calls should require no new
- // allocation.
- for (size_t i = 0; i < mPipes.capacity(); ++i)
- mPipes.push_back(0);
+ mPipes.resize(NSLOTS);
if (! params.validateBlock(true))
{
@@ -763,11 +755,11 @@ LLProcess::LLProcess(const LLSDOrParams& params):
apr_file_t* pipe(mProcess.*(members[i]));
if (i == STDIN)
{
- mPipes.replace(i, new WritePipeImpl(desc, pipe));
+ mPipes[i] = std::make_unique<WritePipeImpl>(desc, pipe);
}
else
{
- mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i)));
+ mPipes[i] = std::make_unique<ReadPipeImpl>(desc, pipe, FILESLOT(i));
}
// Removed temporaily for Xcode 7 build tests: error was:
// "error: expression with side effects will be evaluated despite
@@ -1075,14 +1067,14 @@ PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot)
error = STRINGIZE(mDesc << " has no slot " << slot);
return NULL;
}
- if (mPipes.is_null(slot))
+ if (!mPipes[slot])
{
error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe");
return NULL;
}
// Make sure we dynamic_cast in pointer domain so we can test, rather than
// accepting runtime's exception.
- PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]);
+ PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(mPipes[slot].get());
if (! ppipe)
{
error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name());
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h
index 52b5e0f562..773a3e97f4 100644
--- a/indra/llcommon/llprocess.h
+++ b/indra/llcommon/llprocess.h
@@ -31,9 +31,7 @@
#include "llsdparam.h"
#include "llexception.h"
#include "apr_thread_proc.h"
-#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/optional.hpp>
-#include <boost/noncopyable.hpp>
#include <iosfwd> // std::ostream
#if LL_WINDOWS
@@ -67,7 +65,7 @@ typedef std::shared_ptr<LLProcess> LLProcessPtr;
* indra/llcommon/tests/llprocess_test.cpp for an example of waiting for
* child-process termination in a standalone test context.
*/
-class LL_COMMON_API LLProcess: public boost::noncopyable
+class LL_COMMON_API LLProcess
{
LOG_CLASS(LLProcess);
public:
@@ -547,6 +545,10 @@ public:
static std::string basename(const std::string& path);
static std::string getline(std::istream&);
+ // Non-copyable
+ LLProcess(const LLProcess&) = delete;
+ LLProcess& operator=(const LLProcess&) = delete;
+
private:
/// constructor is private: use create() instead
LLProcess(const LLSDOrParams& params);
@@ -569,7 +571,7 @@ private:
bool mAutokill, mAttached;
Status mStatus;
// explicitly want this ptr_vector to be able to store NULLs
- typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
+ typedef std::vector<std::unique_ptr<BasePipe>> PipeVector;
PipeVector mPipes;
apr_pool_t* mPool;
};
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index 1696a165a2..bb3a35a1e3 100644
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -439,13 +439,21 @@ static F64 calculate_cpu_frequency(U32 measure_msecs)
//// completed now (serialization)
//__asm cpuid
int cpu_info[4] = {-1};
+#if _M_ARM64
+ std::fill(cpu_info, cpu_info + 4, 0);
+#else
__cpuid(cpu_info, 0);
+#endif
// We ask the high-res timer for the start time
QueryPerformanceCounter((LARGE_INTEGER *) &starttime);
// Then we get the current cpu clock and store it
+#if _M_ARM64
+ start = _ReadStatusReg(ARM64_PMCCNTR_EL0);
+#else
start = __rdtsc();
+#endif
// Now we wart for some msecs
_Delay(measure_msecs);
@@ -455,7 +463,11 @@ static F64 calculate_cpu_frequency(U32 measure_msecs)
QueryPerformanceCounter((LARGE_INTEGER *) &endtime);
// And also for the end cpu clock
+#if _M_ARM64
+ end = _ReadStatusReg(ARM64_PMCCNTR_EL0);
+#else
end = __rdtsc();
+#endif
// Now we can restore the default process and thread priorities
SetProcessAffinityMask(hProcess, dwProcessMask);
@@ -495,17 +507,21 @@ private:
// the other three array elements. The CPU identification string is
// not in linear order. The code below arranges the information
// in a human readable form.
+#if !_M_ARM64
int cpu_info[4] = {-1};
__cpuid(cpu_info, 0);
unsigned int ids = (unsigned int)cpu_info[0];
setConfig(eMaxID, (S32)ids);
+#endif
char cpu_vendor[0x20];
memset(cpu_vendor, 0, sizeof(cpu_vendor));
+#if !_M_ARM64
*((int*)cpu_vendor) = cpu_info[1];
*((int*)(cpu_vendor+4)) = cpu_info[3];
*((int*)(cpu_vendor+8)) = cpu_info[2];
setInfo(eVendor, cpu_vendor);
+#endif
std::string cmp_vendor(cpu_vendor);
bool is_amd = false;
if (cmp_vendor == "AuthenticAMD")
@@ -513,6 +529,20 @@ private:
is_amd = true;
}
+#if _M_ARM64
+ HKEY hKey;
+ DWORD gotType;
+ char inBuffer[48] = "";
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ if (!RegQueryValueExA(hKey, "ProcessorNameString", nullptr, &gotType, (PBYTE)(inBuffer), nullptr))
+ {
+ if ((gotType == REG_SZ) && strlen(inBuffer))
+ setInfo(eModel, inBuffer);
+ }
+ RegCloseKey(hKey);
+ }
+#else
// Get the information associated with each valid Id
for(unsigned int i=0; i<=ids; ++i)
{
@@ -623,6 +653,7 @@ private:
setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff);
}
}
+#endif
}
};
@@ -638,7 +669,8 @@ public:
{
getCPUIDInfo();
uint64_t frequency = getSysctlInt64("hw.cpufrequency");
- if (!frequency) {
+ if (!frequency)
+ {
auto tbfrequency = getSysctlInt64("hw.tbfrequency");
struct clockinfo clockrate;
auto clockrate_len = sizeof(clockrate);
@@ -648,14 +680,14 @@ public:
setInfo(eFrequency, (F64)frequency / (F64)1000000);
}
- virtual ~LLProcessorInfoDarwinImpl() {}
+ virtual ~LLProcessorInfoDarwinImpl() = default;
private:
int getSysctlInt(const char* name)
{
int result = 0;
size_t len = sizeof(int);
- int error = sysctlbyname(name, (void*)&result, &len, NULL, 0);
+ int error = sysctlbyname(name, (void*)&result, &len, nullptr, 0);
return error == -1 ? 0 : result;
}
@@ -663,7 +695,7 @@ private:
{
uint64_t value = 0;
size_t size = sizeof(value);
- int result = sysctlbyname(name, (void*)&value, &size, NULL, 0);
+ int result = sysctlbyname(name, (void*)&value, &size, nullptr, 0);
if ( result == 0 )
{
if ( size == sizeof( uint64_t ) )
@@ -732,34 +764,7 @@ private:
}
}
- // *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be
- // The feature bits I think it is. Here's a test:
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- #if defined(__i386__) && defined(__PIC__)
- /* %ebx may be the PIC register. */
- #define __cpuid(level, a, b, c, d) \
- __asm__ ("xchgl\t%%ebx, %1\n\t" \
- "cpuid\n\t" \
- "xchgl\t%%ebx, %1\n\t" \
- : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
- : "0" (level))
- #else
- #define __cpuid(level, a, b, c, d) \
- __asm__ ("cpuid\n\t" \
- : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
- : "0" (level))
- #endif
-
-#if __i386__ || __x86_64__
- unsigned int eax, ebx, ecx, edx;
- __cpuid(0x1, eax, ebx, ecx, edx);
- if(feature_infos[0] != (S32)edx)
- {
- LL_WARNS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL;
- }
-#endif // __i386__ || __x86_64__
-#endif // LL_RELEASE_FOR_DOWNLOAD
-
+ // @TODO: Audit our usage of machdep.cpu.feature_bits.
uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits");
S32 *ext_feature_infos = (S32*)(&ext_feature_info);
@@ -978,7 +983,6 @@ private:
}
fclose(cpuinfo_fp);
}
-# if LL_X86
F64 mhzFromSys = getCPUMaxMHZ();
F64 mhzFromProc {};
@@ -993,6 +997,7 @@ private:
setInfo(eFrequency,(F64)(mhzFromProc));
}
+# if LL_X86
LLPI_SET_INFO_STRING(eBrandName, "model name");
LLPI_SET_INFO_STRING(eVendor, "vendor_id");
diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h
index f8ccf686c8..b955f7f55c 100644
--- a/indra/llcommon/llprocessor.h
+++ b/indra/llcommon/llprocessor.h
@@ -28,20 +28,7 @@
#ifndef LLPROCESSOR_H
#define LLPROCESSOR_H
#include "llunits.h"
-
-#if LL_MSVC && _M_X64
-# define LL_X86_64 1
-# define LL_X86 1
-#elif LL_MSVC && _M_IX86
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
-# define LL_X86_64 1
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__i386__) )
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
-# define LL_PPC 1
-#endif
+#include "llpreprocessor.h"
class LLProcessorInfoImpl;
diff --git a/indra/llcommon/llprocinfo.h b/indra/llcommon/llprocinfo.h
index 5955799812..0fc8b9dbe4 100644
--- a/indra/llcommon/llprocinfo.h
+++ b/indra/llcommon/llprocinfo.h
@@ -51,10 +51,10 @@ public:
typedef U64 time_type; /// Relative microseconds
private:
- LLProcInfo(); // Not defined
- ~LLProcInfo(); // Not defined
- LLProcInfo(const LLProcInfo &); // Not defined
- void operator=(const LLProcInfo &); // Not defined
+ LLProcInfo() = delete;
+ ~LLProcInfo() = delete;
+ LLProcInfo(const LLProcInfo&) = delete;
+ LLProcInfo& operator=(const LLProcInfo&) = delete;
public:
/// Get accumulated system and user CPU time in
diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h
index f6a4d24747..fa94f78019 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -71,26 +71,21 @@
#define LL_PROFILER_CONFIG_TRACY_FAST_TIMER 3 // Profiling on: Fast Timers + Tracy
#ifndef LL_PROFILER_CONFIGURATION
-#define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER
+#define LL_PROFILER_CONFIGURATION 1
#endif
-extern thread_local bool gProfilerEnabled;
-
#if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE)
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#include "tracy/Tracy.hpp"
- // Enable OpenGL profiling
- #define LL_PROFILER_ENABLE_TRACY_OPENGL 0
-
// Enable RenderDoc labeling
- #define LL_PROFILER_ENABLE_RENDER_DOC 0
+ //#define LL_PROFILER_ENABLE_RENDER_DOC 0
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY
#define LL_PROFILER_FRAME_END FrameMark
- #define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
+ #define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name );
#define LL_RECORD_BLOCK_TIME(name) ZoneScoped // Want descriptive names; was: ZoneNamedN( ___tracy_scoped_zone, #name, true );
#define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, name, true );
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, true ) // RGB
@@ -133,7 +128,7 @@ extern thread_local bool gProfilerEnabled;
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define LL_PROFILER_FRAME_END FrameMark
- #define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name ); gProfilerEnabled = true;
+ #define LL_PROFILER_SET_THREAD_NAME( name ) tracy::SetThreadName( name );
#define LL_RECORD_BLOCK_TIME(name) ZoneNamedN(___tracy_scoped_zone, #name, true); const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(name)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
#define LL_PROFILE_ZONE_NAMED(name) ZoneNamedN( ___tracy_scoped_zone, #name, true );
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) ZoneNamedNC( ___tracy_scopped_zone, name, color, true ) // RGB
@@ -155,26 +150,45 @@ extern thread_local bool gProfilerEnabled;
#else
#define LL_PROFILER_FRAME_END
#define LL_PROFILER_SET_THREAD_NAME( name ) (void)(name)
+
+ #define LL_PROFILER_FRAME_END
+ #define LL_PROFILER_SET_THREAD_NAME( name ) (void)(name)
+ #define LL_RECORD_BLOCK_TIME(name)
+ #define LL_PROFILE_ZONE_NAMED(name) // LL_PROFILE_ZONE_NAMED is a no-op when Tracy is disabled
+ #define LL_PROFILE_ZONE_NAMED_COLOR(name,color) // LL_PROFILE_ZONE_NAMED_COLOR is a no-op when Tracy is disabled
+ #define LL_PROFILE_ZONE_SCOPED // LL_PROFILE_ZONE_SCOPED is a no-op when Tracy is disabled
+ #define LL_PROFILE_ZONE_COLOR(name,color) // LL_RECORD_BLOCK_TIME(name)
+
+ #define LL_PROFILE_ZONE_NUM( val ) (void)( val ); // Not supported
+ #define LL_PROFILE_ZONE_TEXT( text, size ) (void)( text ); void( size ); // Not supported
+
+ #define LL_PROFILE_ZONE_ERR(name) (void)(name); // Not supported
+ #define LL_PROFILE_ZONE_INFO(name) (void)(name); // Not supported
+ #define LL_PROFILE_ZONE_WARN(name) (void)(name); // Not supported
+
+ #define LL_PROFILE_MUTEX(type, varname) type varname
+ #define LL_PROFILE_MUTEX_NAMED(type, varname, desc) type varname
+ #define LL_PROFILE_MUTEX_SHARED(type, varname) type varname
+ #define LL_PROFILE_MUTEX_SHARED_NAMED(type, varname, desc) type varname
+ #define LL_PROFILE_MUTEX_LOCK(varname) // LL_PROFILE_MUTEX_LOCK is a no-op when Tracy is disabled
+
#endif // LL_PROFILER
#if LL_PROFILER_ENABLE_TRACY_OPENGL
-#define LL_PROFILE_GPU_ZONE(name) TracyGpuZone(name)
-#define LL_PROFILE_GPU_ZONEC(name,color) TracyGpuZoneC(name,color)
+#define LL_PROFILE_GPU_ZONE(name) TracyGpuZone(name)
+#define LL_PROFILE_GPU_ZONEC(name,color) TracyGpuZoneC(name,color)
#define LL_PROFILER_GPU_COLLECT TracyGpuCollect
#define LL_PROFILER_GPU_CONTEXT TracyGpuContext
-
-// disable memory tracking (incompatible with GPU tracing
-#define LL_PROFILE_ALLOC(ptr, size) (void)(ptr); (void)(size);
-#define LL_PROFILE_FREE(ptr) (void)(ptr);
+#define LL_PROFILER_GPU_CONTEXT_NAMED TracyGpuContextName
#else
-#define LL_PROFILE_GPU_ZONE(name) (void)name;
-#define LL_PROFILE_GPU_ZONEC(name,color) (void)name;(void)color;
+#define LL_PROFILE_GPU_ZONE(name) (void)name;
+#define LL_PROFILE_GPU_ZONEC(name,color) (void)name;(void)color;
#define LL_PROFILER_GPU_COLLECT
#define LL_PROFILER_GPU_CONTEXT
+#define LL_PROFILER_GPU_CONTEXT_NAMED(name) (void)name;
+#endif // LL_PROFILER_ENABLE_TRACY_OPENGL
-#define LL_LABEL_OBJECT_GL(type, name, length, label)
-
-#if !LL_DARWIN && LL_PROFILER_CONFIGURATION > 1
+#if LL_PROFILER_CONFIGURATION >= LL_PROFILER_CONFIG_TRACY
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#else
@@ -182,8 +196,6 @@ extern thread_local bool gProfilerEnabled;
#define LL_PROFILE_FREE(ptr) (void)(ptr);
#endif
-#endif
-
#if LL_PROFILER_ENABLE_RENDER_DOC
#define LL_LABEL_OBJECT_GL(type, name, length, label) glObjectLabel(type, name, length, label)
#else
diff --git a/indra/llcommon/llptrto.cpp b/indra/llcommon/llptrto.cpp
index c4528a47a7..adf636c4d2 100644
--- a/indra/llcommon/llptrto.cpp
+++ b/indra/llcommon/llptrto.cpp
@@ -31,10 +31,9 @@
// associated header
#include "llptrto.h"
// STL headers
+#include <type_traits>
// std headers
// external library headers
-#include <boost/type_traits/is_same.hpp>
-#include <boost/static_assert.hpp>
// other Linden headers
#include "llmemory.h"
@@ -76,27 +75,27 @@ public:
int main(int argc, char *argv[])
{
// test LLPtrTo<>
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<Bar>::type, Bar*>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<SubBar>::type, SubBar*>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLPtrTo<int>::type, int*>::value));
+ static_assert((std::is_same_v<LLPtrTo<RCFoo>::type, LLPointer<RCFoo> >));
+ static_assert((std::is_same_v<LLPtrTo<RCSubFoo>::type, LLPointer<RCSubFoo> >));
+ static_assert((std::is_same_v<LLPtrTo<TSRCFoo>::type, LLPointer<TSRCFoo> >));
+ static_assert((std::is_same_v<LLPtrTo<Bar>::type, Bar*>));
+ static_assert((std::is_same_v<LLPtrTo<SubBar>::type, SubBar*>));
+ static_assert((std::is_same_v<LLPtrTo<int>::type, int*>));
// Test LLRemovePointer<>. Note that we remove both pointer variants from
// each kind of type, regardless of whether the variant makes sense.
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCFoo*>::type, RCFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<Bar*>::type, Bar>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<Bar> >::type, Bar>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<SubBar*>::type, SubBar>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer<int*>::type, int>::value));
- BOOST_STATIC_ASSERT((boost::is_same<LLRemovePointer< LLPointer<int> >::type, int>::value));
+ static_assert((std::is_same_v<LLRemovePointer<RCFoo*>::type, RCFoo>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<RCFoo> >::type, RCFoo>));
+ static_assert((std::is_same_v<LLRemovePointer<RCSubFoo*>::type, RCSubFoo>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<RCSubFoo> >::type, RCSubFoo>));
+ static_assert((std::is_same_v<LLRemovePointer<TSRCFoo*>::type, TSRCFoo>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<TSRCFoo> >::type, TSRCFoo>));
+ static_assert((std::is_same_v<LLRemovePointer<Bar*>::type, Bar>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<Bar> >::type, Bar>));
+ static_assert((std::is_same_v<LLRemovePointer<SubBar*>::type, SubBar>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<SubBar> >::type, SubBar>));
+ static_assert((std::is_same_v<LLRemovePointer<int*>::type, int>));
+ static_assert((std::is_same_v<LLRemovePointer< LLPointer<int> >::type, int>));
return 0;
}
diff --git a/indra/llcommon/llptrto.h b/indra/llcommon/llptrto.h
index b57a1ee7f4..24e312559e 100644
--- a/indra/llcommon/llptrto.h
+++ b/indra/llcommon/llptrto.h
@@ -35,8 +35,6 @@
#include "llrefcount.h" // LLRefCount
#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
-#include <boost/type_traits/is_base_of.hpp>
-#include <boost/type_traits/remove_pointer.hpp>
#include <memory> // std::shared_ptr, std::unique_ptr
#include <type_traits>
@@ -58,14 +56,14 @@ struct LLPtrTo
/// specialize for subclasses of LLRefCount
template <class T>
-struct LLPtrTo<T, typename std::enable_if< boost::is_base_of<LLRefCount, T>::value >::type>
+struct LLPtrTo<T, typename std::enable_if< std::is_base_of<LLRefCount, T>::value >::type>
{
typedef LLPointer<T> type;
};
/// specialize for subclasses of LLThreadSafeRefCount
template <class T>
-struct LLPtrTo<T, typename std::enable_if< boost::is_base_of<LLThreadSafeRefCount, T>::value >::type>
+struct LLPtrTo<T, typename std::enable_if< std::is_base_of<LLThreadSafeRefCount, T>::value >::type>
{
typedef LLPointer<T> type;
};
@@ -76,7 +74,7 @@ struct LLPtrTo<T, typename std::enable_if< boost::is_base_of<LLThreadSafeRefCoun
template <typename PTRTYPE>
struct LLRemovePointer
{
- typedef typename boost::remove_pointer<PTRTYPE>::type type;
+ typedef typename std::remove_pointer<PTRTYPE>::type type;
};
/// specialize for LLPointer<SOMECLASS>
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index 0196a24b18..efeeb1340e 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -80,7 +80,7 @@ void LLQueuedThread::shutdown()
mRequestQueue.close();
}
- S32 timeout = 100;
+ S32 timeout = 50;
for ( ; timeout>0; timeout--)
{
if (isStopped())
@@ -101,19 +101,34 @@ void LLQueuedThread::shutdown()
}
QueuedRequest* req;
- S32 active_count = 0;
+ S32 queued_count = 0;
+ bool has_active = false;
+ lockData();
while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
{
- if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
+ if (req->getStatus() == STATUS_INPROGRESS)
+ {
+ has_active = true;
+ req->setFlags(FLAG_ABORT | FLAG_AUTO_COMPLETE);
+ continue;
+ }
+ if (req->getStatus() == STATUS_QUEUED)
{
- ++active_count;
+ ++queued_count;
req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
}
req->deleteRequest();
}
- if (active_count)
+ unlockData();
+ if (queued_count)
+ {
+ LL_WARNS() << "~LLQueuedThread() called with unpocessed requests: " << queued_count << LL_ENDL;
+ }
+ if (has_active)
{
- LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
+ LL_WARNS() << "~LLQueuedThread() called with active requests!" << LL_ENDL;
+ ms_sleep(100); // last chance for request to finish
+ printQueueStats();
}
mRequestQueue.close();
@@ -570,7 +585,12 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3
LLQueuedThread::QueuedRequest::~QueuedRequest()
{
- llassert_always(mStatus == STATUS_DELETE);
+ if (mStatus != STATUS_DELETE)
+ {
+ // The only method to delete a request is deleteRequest(),
+ // it should have set the status to STATUS_DELETE
+ LL_ERRS() << "LLQueuedThread::QueuedRequest deleted with status " << mStatus << LL_ENDL;
+ }
}
//virtual
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index 02d3a96fcc..de50b8ae95 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -117,11 +117,11 @@ public:
virtual ~LLQueuedThread();
virtual void shutdown();
-private:
// No copy constructor or copy assignment
- LLQueuedThread(const LLQueuedThread&);
- LLQueuedThread& operator=(const LLQueuedThread&);
+ LLQueuedThread(const LLQueuedThread&) = delete;
+ LLQueuedThread& operator=(const LLQueuedThread&) = delete;
+private:
virtual bool runCondition(void);
virtual void run(void);
virtual void startThread(void);
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index 3a253d8fa6..93ca7d1d00 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -26,7 +26,6 @@
#ifndef LLREFCOUNT_H
#define LLREFCOUNT_H
-#include <boost/noncopyable.hpp>
#include <boost/intrusive_ptr.hpp>
#include "llatomic.h"
diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp
index 655869a704..a4b45ed80d 100644
--- a/indra/llcommon/llsdjson.cpp
+++ b/indra/llcommon/llsdjson.cpp
@@ -35,7 +35,11 @@
#include "llerror.h"
#include "../llmath/llmath.h"
+#if LL_WINDOWS
+#include <boost/json.hpp>
+#else
#include <boost/json/src.hpp>
+#endif
//=========================================================================
LLSD LlsdFromJson(const boost::json::value& val)
diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp
index 3ae153a67c..caaac3d762 100644
--- a/indra/llcommon/llsdparam.cpp
+++ b/indra/llcommon/llsdparam.cpp
@@ -30,7 +30,6 @@
// Project includes
#include "llsdparam.h"
#include "llsdutil.h"
-#include "boost/bind.hpp"
static LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
@@ -43,8 +42,6 @@ static const LLSD NO_VALUE_MARKER;
LLParamSDParser::LLParamSDParser()
: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
{
- using boost::bind;
-
if (sReadFuncs.empty())
{
registerParserFuncs<LLInitParam::Flag>(readFlag, &LLParamSDParser::writeFlag);
@@ -97,7 +94,7 @@ void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool
mNameStack.clear();
setParseSilently(silent);
- LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack);
+ LLParamSDParserUtilities::readSDValues(std::bind(&LLParamSDParser::submit, this, std::ref(block), std::placeholders::_1, std::placeholders::_2), sd, mNameStack);
//readSDValues(sd, block);
}
@@ -276,14 +273,14 @@ void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLI
}
else if (sd.isUndefined())
{
- if (!cb.empty())
+ if (cb != nullptr)
{
cb(NO_VALUE_MARKER, stack);
}
}
else
{
- if (!cb.empty())
+ if (cb != nullptr)
{
cb(sd, stack);
}
@@ -333,7 +330,7 @@ namespace LLInitParam
if (!p.writeValue<LLSD>(mValue, name_stack_range))
{
// otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc)
- LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack_range);
+ LLParamSDParserUtilities::readSDValues(std::bind(&serializeElement, std::ref(p), std::placeholders::_1, std::placeholders::_2), mValue, name_stack_range);
}
return true;
}
diff --git a/indra/llcommon/llsdparam.h b/indra/llcommon/llsdparam.h
index 21ebb9a258..447ba02327 100644
--- a/indra/llcommon/llsdparam.h
+++ b/indra/llcommon/llsdparam.h
@@ -29,14 +29,14 @@
#define LL_LLSDPARAM_H
#include "llinitparam.h"
-#include "boost/function.hpp"
+#include <functional>
#include "llfasttimer.h"
struct LL_COMMON_API LLParamSDParserUtilities
{
static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range);
- typedef boost::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t;
+ typedef std::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t;
static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack);
static void readSDValues(read_sd_cb_t cb, const LLSD& sd);
};
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 37af366a20..68a7bf0adf 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -37,7 +37,7 @@
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
-#ifdef LL_USESYSTEMLIBS
+#if 1
# include <zlib.h>
#else
# include "zlib-ng/zlib.h" // for davep's dirty little zip functions
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
index 6396caf8d5..ce416baa04 100644
--- a/indra/llcommon/llsdserialize_xml.cpp
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -35,7 +35,7 @@
extern "C"
{
-#ifdef LL_USESYSTEMLIBS
+#if 1
# include <expat.h>
#else
# include "expat/expat.h"
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index 38bbe19ddd..497c0ad3eb 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -553,6 +553,100 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter
} // namespace llsd
+/*****************************************************************************
+* LLSDParam<std::vector<T>>
+*****************************************************************************/
+// Given an LLSD array, return a const std::vector<T>&, where T is a type
+// supported by LLSDParam. Bonus: if the LLSD value is actually a scalar,
+// return a single-element vector containing the converted value.
+template <typename T>
+class LLSDParam<std::vector<T>>: public LLSDParamBase
+{
+public:
+ LLSDParam(const LLSD& array)
+ {
+ // treat undefined "array" as empty vector
+ if (array.isDefined())
+ {
+ // what if it's a scalar?
+ if (! array.isArray())
+ {
+ v.push_back(LLSDParam<T>(array));
+ }
+ else // really is an array
+ {
+ // reserve space for the array entries
+ v.reserve(array.size());
+ for (const auto& item : llsd::inArray(array))
+ {
+ v.push_back(LLSDParam<T>(item));
+ }
+ }
+ }
+ }
+
+ operator const std::vector<T>&() const { return v; }
+
+private:
+ std::vector<T> v;
+};
+
+
+/*****************************************************************************
+ * toArray(), toMap()
+ *****************************************************************************/
+namespace llsd
+{
+
+// For some T convertible to LLSD, given std::vector<T> myVec,
+// toArray(myVec) returns an LLSD array whose entries correspond to the
+// items in myVec.
+// For some U convertible to LLSD, given function U xform(const T&),
+// toArray(myVec, xform) returns an LLSD array whose every entry is
+// xform(item) of the corresponding item in myVec.
+// toArray() actually works with any container<C> usable with range
+// 'for', not just std::vector.
+// (Once we get C++20 we can use std::identity instead of this default lambda.)
+template<typename C, typename FUNC>
+LLSD toArray(const C& container, FUNC&& func = [](const auto& arg) { return arg; })
+{
+ LLSD array;
+ for (const auto& item : container)
+ {
+ array.append(std::forward<FUNC>(func)(item));
+ }
+ return array;
+}
+
+// For some T convertible to LLSD, given std::map<std::string, T> myMap,
+// toMap(myMap) returns an LLSD map whose entries correspond to the
+// (key, value) pairs in myMap.
+// For some U convertible to LLSD, given function
+// std::pair<std::string, U> xform(const std::pair<std::string, T>&),
+// toMap(myMap, xform) returns an LLSD map whose every entry is
+// xform(pair) of the corresponding (key, value) pair in myMap.
+// toMap() actually works with any container usable with range 'for', not
+// just std::map. It need not even be an associative container, as long as
+// you pass an xform function that returns std::pair<std::string, U>.
+// (Once we get C++20 we can use std::identity instead of this default lambda.)
+template<typename C, typename FUNC>
+LLSD toMap(const C& container, FUNC&& func = [](const auto& arg) { return arg; })
+{
+ LLSD map;
+ for (const auto& pair : container)
+ {
+ const auto& [key, value] = std::forward<FUNC>(func)(pair);
+ map[key] = value;
+ }
+ return map;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+ * boost::hash<LLSD>
+ *****************************************************************************/
+
// Specialization for generating a hash value from an LLSD block.
namespace boost
{
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index b5659e053c..3fba8602ee 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -25,7 +25,6 @@
#ifndef LLSINGLETON_H
#define LLSINGLETON_H
-#include <boost/noncopyable.hpp>
#include <boost/unordered_set.hpp>
#include <initializer_list>
#include <list>
@@ -43,11 +42,14 @@
#pragma warning(disable : 4506) // no definition for inline function
#endif
-class LLSingletonBase: private boost::noncopyable
+class LLSingletonBase
{
public:
class MasterList;
+ LLSingletonBase(const LLSingletonBase&) = delete;
+ LLSingletonBase& operator=(const LLSingletonBase&) = delete;
+
private:
// All existing LLSingleton instances are tracked in this master list.
typedef std::list<LLSingletonBase*> list_t;
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 07adf71d18..bb6d091a97 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -260,7 +260,7 @@ S32 utf16str_wstring_length(const llutf16string &utf16str, const S32 utf16_len)
{
S32 surrogate_pairs = 0;
// ... craziness to make gcc happy (llutf16string.c_str() is tweaked on linux):
- const U16 *const utf16_chars = &(*(utf16str.begin()));
+ const auto *const utf16_chars = &(*(utf16str.begin()));
S32 i = 0;
while (i < utf16_len)
{
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 7a8edc176d..4023294fff 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -635,7 +635,11 @@ LL_COMMON_API std::string rawstr_to_utf8(const std::string& raw);
//
// This typedef may or may not be identical to std::wstring, depending on
// LL_WCHAR_T_NATIVE.
+#if __FreeBSD__
+typedef std::basic_string<char16_t> llutf16string;
+#else
typedef std::basic_string<U16> llutf16string;
+#endif
// Considering wchar_t, llwchar and U16, there are three relevant cases:
#if LLWCHAR_IS_WCHAR_T // every which way but Windows
@@ -1235,9 +1239,9 @@ void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<strin
// (unless there ARE no escapes).
std::unique_ptr< LLStringUtilBaseImpl::InString<T> > instrp;
if (escapes.empty())
- instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end()));
+ instrp = std::make_unique<LLStringUtilBaseImpl::InString<T>>(string.begin(), string.end());
else
- instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes));
+ instrp = std::make_unique<LLStringUtilBaseImpl::InEscString<T>>(string.begin(), string.end(), escapes);
LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes);
}
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 94d59adf29..542de4ee67 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -33,7 +33,7 @@
#include "llsys.h"
#include <iostream>
-#ifdef LL_USESYSTEMLIBS
+#if 1
# include <zlib.h>
#else
# include "zlib-ng/zlib.h"
@@ -51,9 +51,6 @@
#include <boost/circular_buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/type_traits/is_integral.hpp>
-#include <boost/type_traits/is_float.hpp>
#include "llfasttimer.h"
using namespace llsd;
@@ -197,6 +194,9 @@ LLOSInfo::LLOSInfo() :
GetSystemInfo(&si); //if it fails get regular system info
//(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load)
+#pragma warning(push)
+#pragma warning(disable : 4996) // ignore 'deprecated.' GetVersionEx is deprecated
+
// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
@@ -215,6 +215,8 @@ LLOSInfo::LLOSInfo() :
}
}
+#pragma warning(pop)
+
S32 ubr = 0; // Windows 10 Update Build Revision, can be retrieved from a registry
if (mMajorVer == 10)
{
@@ -722,7 +724,7 @@ public:
// Store every integer type as LLSD::Integer.
template <class T>
void add(const LLSD::String& name, const T& value,
- typename boost::enable_if<boost::is_integral<T> >::type* = 0)
+ typename std::enable_if_t<std::is_integral_v<T> >* = 0)
{
mStats[name] = LLSD::Integer(value);
}
@@ -730,7 +732,7 @@ public:
// Store every floating-point type as LLSD::Real.
template <class T>
void add(const LLSD::String& name, const T& value,
- typename boost::enable_if<boost::is_float<T> >::type* = 0)
+ typename std::enable_if_t<std::is_floating_point_v<T> >* = 0)
{
mStats[name] = LLSD::Real(value);
}
@@ -1332,7 +1334,7 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
S32 bytes = 0;
tmpfile = dstfile + ".t";
#ifdef LL_WINDOWS
- llutf16string utf16filename = utf8str_to_utf16str(srcfile);
+ std::wstring utf16filename = ll_convert<std::wstring>(srcfile);
src = gzopen_w(utf16filename.c_str(), "rb");
#else
src = gzopen(srcfile.c_str(), "rb");
@@ -1352,10 +1354,6 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
} while(gzeof(src) == 0);
fclose(dst);
dst = NULL;
-#if LL_WINDOWS
- // Rename in windows needs the dstfile to not exist.
- LLFile::remove(dstfile, ENOENT);
-#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:
@@ -1376,7 +1374,7 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
tmpfile = dstfile + ".t";
#ifdef LL_WINDOWS
- llutf16string utf16filename = utf8str_to_utf16str(tmpfile);
+ std::wstring utf16filename = ll_convert<std::wstring>(tmpfile);
dst = gzopen_w(utf16filename.c_str(), "wb");
#else
dst = gzopen(tmpfile.c_str(), "wb");
@@ -1403,10 +1401,6 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
gzclose(dst);
dst = NULL;
-#if LL_WINDOWS
- // Rename in windows needs the dstfile to not exist.
- LLFile::remove(dstfile);
-#endif
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
retval = true;
err:
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index e5d25b52f0..8c12ee7f12 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -28,6 +28,7 @@
#include "apr_portable.h"
+#include "llapp.h"
#include "llthread.h"
#include "llmutex.h"
@@ -35,6 +36,7 @@
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "llexception.h"
+#include "workqueue.h"
#if LL_LINUX
#include <sched.h>
@@ -106,6 +108,27 @@ namespace
return s_thread_id;
}
+#if LL_WINDOWS
+
+ static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+ U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
+ {
+ if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
+ {
+ // Handled
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // handle it, convert to std::exception
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+#endif // LL_WINDOWS
} // anonymous namespace
LL_COMMON_API bool on_main_thread()
@@ -157,20 +180,11 @@ void LLThread::threadRun()
// Run the user supplied function
do
{
- try
- {
- run();
- }
- catch (const LLContinueError &e)
- {
- LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
- "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
- //output possible call stacks to log file.
- LLError::LLCallStacks::print();
-
- LOG_UNHANDLED_EXCEPTION("LLThread");
- continue;
- }
+#ifdef LL_WINDOWS
+ sehHandle(); // Structured Exception Handling
+#else
+ tryRun();
+#endif
break;
} while (true);
@@ -188,6 +202,73 @@ void LLThread::threadRun()
mStatus = STOPPED;
}
+void LLThread::tryRun()
+{
+ try
+ {
+ run();
+ }
+ catch (const LLContinueError& e)
+ {
+ LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
+ "'. Error what is: '" << e.what() << "'" << LL_ENDL;
+ LLError::LLCallStacks::print();
+
+ LOG_UNHANDLED_EXCEPTION("LLThread");
+ }
+ catch (std::bad_alloc&)
+ {
+ // Todo: improve this, this is going to have a different callstack
+ // instead of showing where it crashed
+ LL_WARNS("THREAD") << "Out of memory in a thread: " << mName << LL_ENDL;
+
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ []() {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("THREAD") << "Out of memory in a thread" << LL_ENDL;
+ });
+ }
+#ifndef LL_WINDOWS
+ catch (...)
+ {
+ // Stash any other kind of uncaught exception to be rethrown by main thread.
+ LL_WARNS("THREAD") << "Capturing and rethrowing uncaught exception in LLThread "
+ << mName << LL_ENDL;
+
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ [exc = std::current_exception(), name = mName]()
+ {
+ LL_INFOS("THREAD") << "Rethrowing exception from thread " << name << LL_ENDL;
+ std::rethrow_exception(exc);
+ });
+ }
+#endif // else LL_WINDOWS
+}
+
+#ifdef LL_WINDOWS
+void LLThread::sehHandle()
+{
+ __try
+ {
+ // handle stop and continue exceptions first
+ tryRun();
+ }
+ __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // convert to C++ styled exception
+ // Note: it might be better to use _se_set_translator
+ // if you want exception to inherit full callstack
+ char integer_string[512];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+}
+#endif
+
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
mPaused(false),
mName(name),
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index 4194e0014d..8794ac93aa 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -68,7 +68,7 @@ public:
// Called from MAIN THREAD.
void pause();
void unpause();
- bool isPaused() { return isStopped() || mPaused; }
+ bool isPaused() const { return isStopped() || mPaused; }
// Cause the thread to wake up and check its condition
void wake();
@@ -97,6 +97,11 @@ private:
// static function passed to APR thread creation routine
void threadRun();
+ void tryRun();
+
+#ifdef LL_WINDOWS
+ void sehHandle();
+#endif
protected:
std::string mName;
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
index 85fcbbacac..6532439dd9 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -452,11 +452,9 @@ ElementT LLThreadSafeQueue<ElementT, QueueT>::pop(void)
// so we can finish draining the queue.
pop_result popped = pop_(lock1, value);
if (popped == POPPED)
-#if LL_LINUX
return value;
-#else
- return std::move(value);
-#endif
+ // don't use std::move when returning local value because
+ // it prevents the compiler from optimizing with copy elision
// Once the queue is DONE, there will never be any more coming.
if (popped == DONE)
diff --git a/indra/llcommon/lltreeiterators.h b/indra/llcommon/lltreeiterators.h
index cef501b987..cc13955d2f 100644
--- a/indra/llcommon/lltreeiterators.h
+++ b/indra/llcommon/lltreeiterators.h
@@ -60,10 +60,10 @@
#define LL_LLTREEITERATORS_H
#include "llptrto.h"
+#include <functional>
#include <vector>
#include <deque>
#include <boost/iterator/iterator_facade.hpp>
-#include <boost/function.hpp>
#include <boost/static_assert.hpp>
namespace LLTreeIter
@@ -93,7 +93,7 @@ protected:
typedef typename LLPtrTo<NODE>::type ptr_type;
/// function that advances from this node to next accepts a node pointer
/// and returns another
- typedef boost::function<ptr_type(const ptr_type&)> func_type;
+ typedef std::function<ptr_type(const ptr_type&)> func_type;
typedef SELFTYPE self_type;
};
@@ -330,7 +330,7 @@ protected:
typedef typename super::ptr_type ptr_type;
// The func_type is different for this: from a NODE pointer, we must
// obtain a CHILDITER.
- typedef boost::function<CHILDITER(const ptr_type&)> func_type;
+ typedef std::function<CHILDITER(const ptr_type&)> func_type;
private:
typedef std::vector<ptr_type> list_type;
public:
@@ -435,7 +435,7 @@ protected:
typedef typename super::ptr_type ptr_type;
// The func_type is different for this: from a NODE pointer, we must
// obtain a CHILDITER.
- typedef boost::function<CHILDITER(const ptr_type&)> func_type;
+ typedef std::function<CHILDITER(const ptr_type&)> func_type;
private:
// Upon reaching a given node in our pending list, we need to know whether
// we've already pushed that node's children, so we must associate a bool
@@ -574,7 +574,7 @@ protected:
typedef typename super::ptr_type ptr_type;
// The func_type is different for this: from a NODE pointer, we must
// obtain a CHILDITER.
- typedef boost::function<CHILDITER(const ptr_type&)> func_type;
+ typedef std::function<CHILDITER(const ptr_type&)> func_type;
private:
// We need a FIFO queue rather than a LIFO stack. Use a deque rather than
// a vector, since vector can't implement pop_front() efficiently.
diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp
index 33a48d970d..1d246bb70e 100644
--- a/indra/llcommon/lluriparser.cpp
+++ b/indra/llcommon/lluriparser.cpp
@@ -33,7 +33,7 @@ LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedT
{
if (u.find("://") == std::string::npos)
{
- mNormalizedUri = "http://";
+ mNormalizedUri = "https://";
mTmpScheme = true;
}
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index 6d7cf473f5..3fbc45baaf 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -174,14 +174,6 @@ void LLUUID::toString(std::string& out) const
(U8)(mData[15]));
}
-// *TODO: deprecate
-void LLUUID::toString(char* out) const
-{
- std::string buffer;
- toString(buffer);
- strcpy(out, buffer.c_str()); /* Flawfinder: ignore */
-}
-
void LLUUID::toCompressedString(std::string& out) const
{
char bytes[UUID_BYTES + 1];
@@ -190,13 +182,6 @@ void LLUUID::toCompressedString(std::string& out) const
out.assign(bytes, UUID_BYTES);
}
-// *TODO: deprecate
-void LLUUID::toCompressedString(char* out) const
-{
- memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */
- out[UUID_BYTES] = '\0';
-}
-
std::string LLUUID::getString() const
{
return asString();
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index bd4edc7993..ca1cf03c4d 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -103,9 +103,7 @@ public:
friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid);
friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid);
- void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0)
void toString(std::string& out) const;
- void toCompressedString(char *out) const; // Does not allocate memory, needs 17 characters (including \0)
void toCompressedString(std::string& out) const;
std::string asString() const;
diff --git a/indra/llcommon/llwatchdog.cpp b/indra/llcommon/llwatchdog.cpp
new file mode 100644
index 0000000000..fa240a9ed7
--- /dev/null
+++ b/indra/llcommon/llwatchdog.cpp
@@ -0,0 +1,290 @@
+/**
+ * @file llthreadwatchdog.cpp
+ * @brief The LLThreadWatchdog class definitions
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+
+#include "llwatchdog.h"
+#include "llthread.h"
+
+constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U;
+
+// This class runs the watchdog timing thread.
+class LLWatchdogTimerThread : public LLThread
+{
+public:
+ LLWatchdogTimerThread() :
+ LLThread("Watchdog"),
+ mSleepMsecs(0),
+ mStopping(false)
+ {
+ }
+
+ ~LLWatchdogTimerThread() {}
+
+ void setSleepTime(long ms) { mSleepMsecs = ms; }
+ void stop()
+ {
+ mStopping = true;
+ mSleepMsecs = 1;
+ }
+
+ void run() override
+ {
+ while(!mStopping)
+ {
+ LLWatchdog::getInstance()->run();
+ ms_sleep(mSleepMsecs);
+ }
+ }
+
+private:
+ long mSleepMsecs;
+ bool mStopping;
+};
+
+// LLWatchdogEntry
+LLWatchdogEntry::LLWatchdogEntry(const std::string& thread_name)
+ : mThreadName(thread_name)
+ , mThreadID(LLThread::currentID())
+{
+}
+
+LLWatchdogEntry::~LLWatchdogEntry()
+{
+ stop();
+}
+
+void LLWatchdogEntry::start()
+{
+ LLWatchdog::getInstance()->add(this);
+}
+
+void LLWatchdogEntry::stop()
+{
+ // this can happen very late in the shutdown sequence
+ if (!LLWatchdog::wasDeleted())
+ {
+ LLWatchdog::getInstance()->remove(this);
+ }
+}
+std::string LLWatchdogEntry::getThreadName() const
+{
+ return mThreadName + llformat(": %d", mThreadID);
+}
+
+// LLWatchdogTimeout
+const std::string UNINIT_STRING = "uninitialized";
+
+LLWatchdogTimeout::LLWatchdogTimeout(const std::string& thread_name) :
+ LLWatchdogEntry(thread_name),
+ mTimeout(0.0f),
+ mPingState(UNINIT_STRING)
+{
+}
+
+LLWatchdogTimeout::~LLWatchdogTimeout()
+{
+}
+
+bool LLWatchdogTimeout::isAlive() const
+{
+ return (mTimer.getStarted() && !mTimer.hasExpired());
+}
+
+void LLWatchdogTimeout::reset()
+{
+ mTimer.setTimerExpirySec(mTimeout);
+}
+
+void LLWatchdogTimeout::setTimeout(F32 d)
+{
+ mTimeout = d;
+}
+
+void LLWatchdogTimeout::start(std::string_view state)
+{
+ if (mTimeout == 0)
+ {
+ LL_WARNS() << "Cant' start watchdog entry - no timeout set" << LL_ENDL;
+ return;
+ }
+ // Order of operation is very important here.
+ // After LLWatchdogEntry::start() is called
+ // LLWatchdogTimeout::isAlive() will be called asynchronously.
+ ping(state);
+ mTimer.start();
+ mTimer.setTimerExpirySec(mTimeout); // timer expiration set to 0 by start()
+ LLWatchdogEntry::start();
+}
+
+void LLWatchdogTimeout::stop()
+{
+ LLWatchdogEntry::stop();
+ mTimer.stop();
+}
+
+void LLWatchdogTimeout::ping(std::string_view state)
+{
+ if (!state.empty())
+ {
+ mPingState = state;
+ }
+ reset();
+}
+
+// LLWatchdog
+LLWatchdog::LLWatchdog()
+ :mSuspectsAccessMutex()
+ ,mTimer(nullptr)
+ ,mLastClockCount(0)
+{
+}
+
+LLWatchdog::~LLWatchdog()
+{
+}
+
+void LLWatchdog::add(LLWatchdogEntry* e)
+{
+ lockThread();
+ mSuspects.insert(e);
+ unlockThread();
+}
+
+void LLWatchdog::remove(LLWatchdogEntry* e)
+{
+ lockThread();
+ mSuspects.erase(e);
+ unlockThread();
+}
+
+void LLWatchdog::init(func_t set_error_state_callback)
+{
+ if (!mSuspectsAccessMutex && !mTimer)
+ {
+ mSuspectsAccessMutex = new LLMutex();
+ mTimer = new LLWatchdogTimerThread();
+ mTimer->setSleepTime(WATCHDOG_SLEEP_TIME_USEC / 1000);
+ mLastClockCount = LLTimer::getTotalTime();
+
+ // mTimer->start() kicks off the thread, any code after
+ // start needs to use the mSuspectsAccessMutex
+ mTimer->start();
+ }
+ mCreateMarkerFnc = set_error_state_callback;
+}
+
+void LLWatchdog::cleanup()
+{
+ if (mTimer)
+ {
+ mTimer->stop();
+ delete mTimer;
+ mTimer = nullptr;
+ }
+
+ if (mSuspectsAccessMutex)
+ {
+ delete mSuspectsAccessMutex;
+ mSuspectsAccessMutex = nullptr;
+ }
+
+ mLastClockCount = 0;
+}
+
+void LLWatchdog::run()
+{
+ lockThread();
+
+ // Check the time since the last call to run...
+ // If the time elapsed is two times greater than the regualr sleep time
+ // reset the active timeouts.
+ constexpr U32 TIME_ELAPSED_MULTIPLIER = 2;
+ U64 current_time = LLTimer::getTotalTime();
+ U64 current_run_delta = current_time - mLastClockCount;
+ mLastClockCount = current_time;
+
+ if (current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER))
+ {
+ LL_INFOS() << "Watchdog thread delayed: resetting entries." << LL_ENDL;
+ for (const auto& suspect : mSuspects)
+ {
+ suspect->reset();
+ }
+ }
+ else
+ {
+ SuspectsRegistry::iterator result =
+ std::find_if(mSuspects.begin(),
+ mSuspects.end(),
+ [](const LLWatchdogEntry* suspect){ return ! suspect->isAlive(); });
+ if (result != mSuspects.end())
+ {
+ // error!!!
+ if(mTimer)
+ {
+ mTimer->stop();
+ }
+
+ // Sets error marker file
+ mCreateMarkerFnc();
+ // Todo1: Warn user?
+ // Todo2: We probably want to report even if 5 seconds passed, just not error 'yet'.
+ std::string last_state = (*result)->getLastState();
+ if (last_state.empty())
+ {
+ LL_ERRS() << "Watchdog timer for thread " << (*result)->getThreadName()
+ << " expired; assuming viewer is hung and crashing" << LL_ENDL;
+ }
+ else
+ {
+ LL_ERRS() << "Watchdog timer for thread " << (*result)->getThreadName()
+ << " expired with state: " << last_state
+ << "; assuming viewer is hung and crashing" << LL_ENDL;
+ }
+ }
+ }
+
+
+ unlockThread();
+}
+
+void LLWatchdog::lockThread()
+{
+ if (mSuspectsAccessMutex)
+ {
+ mSuspectsAccessMutex->lock();
+ }
+}
+
+void LLWatchdog::unlockThread()
+{
+ if (mSuspectsAccessMutex)
+ {
+ mSuspectsAccessMutex->unlock();
+ }
+}
diff --git a/indra/llcommon/llwatchdog.h b/indra/llcommon/llwatchdog.h
new file mode 100644
index 0000000000..fded881bb8
--- /dev/null
+++ b/indra/llcommon/llwatchdog.h
@@ -0,0 +1,118 @@
+/**
+ * @file llthreadwatchdog.h
+ * @brief The LLThreadWatchdog class declaration
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTHREADWATCHDOG_H
+#define LL_LLTHREADWATCHDOG_H
+
+#ifndef LL_TIMER_H
+ #include "lltimer.h"
+#endif
+#include "llmutex.h"
+#include "llsingleton.h"
+
+#include <functional>
+
+// LLWatchdogEntry is the interface used by the tasks that
+// need to be watched.
+class LLWatchdogEntry
+{
+public:
+ LLWatchdogEntry(const std::string &thread_name);
+ virtual ~LLWatchdogEntry();
+
+ // isAlive is accessed by the watchdog thread.
+ // This may mean that resources used by
+ // isAlive and other method may need synchronization.
+ virtual bool isAlive() const = 0;
+ virtual void reset() = 0;
+ virtual void start();
+ virtual void stop();
+ virtual std::string getLastState() const { return std::string(); }
+ typedef std::thread::id id_t;
+ std::string getThreadName() const;
+
+private:
+ id_t mThreadID; // ID of the thread being watched
+ std::string mThreadName;
+};
+
+class LLWatchdogTimeout : public LLWatchdogEntry
+{
+public:
+ LLWatchdogTimeout(const std::string& thread_name);
+ virtual ~LLWatchdogTimeout();
+
+ bool isAlive() const override;
+ void reset() override;
+ void start() override { start(""); }
+ void stop() override;
+
+ void start(std::string_view state);
+ void setTimeout(F32 d);
+ void ping(std::string_view state);
+ const std::string& getState() {return mPingState; }
+ std::string getLastState() const override { return mPingState; }
+
+private:
+ LLTimer mTimer;
+ F32 mTimeout;
+ std::string mPingState;
+};
+
+class LLWatchdogTimerThread; // Defined in the cpp
+class LLWatchdog : public LLSingleton<LLWatchdog>
+{
+ LLSINGLETON(LLWatchdog);
+ ~LLWatchdog();
+
+public:
+ // Add an entry to the watchdog.
+ void add(LLWatchdogEntry* e);
+ void remove(LLWatchdogEntry* e);
+
+ typedef std::function<void()> func_t;
+ void init(func_t set_error_state_callback);
+ void run();
+ void cleanup();
+
+
+private:
+ void lockThread();
+ void unlockThread();
+
+ typedef std::set<LLWatchdogEntry*> SuspectsRegistry;
+ SuspectsRegistry mSuspects;
+ LLMutex* mSuspectsAccessMutex;
+ LLWatchdogTimerThread* mTimer;
+ U64 mLastClockCount;
+
+ // At the moment watchdog expects app to set markers in mCreateMarkerFnc,
+ // but technically can be used to set any error states or do some cleanup
+ // or show warnings.
+ func_t mCreateMarkerFnc;
+};
+
+#endif // LL_LLTHREADWATCHDOG_H
diff --git a/indra/llcommon/llwin32headers.h b/indra/llcommon/llwin32headers.h
index df433deb7a..32139821d5 100644
--- a/indra/llcommon/llwin32headers.h
+++ b/indra/llcommon/llwin32headers.h
@@ -29,6 +29,7 @@
#ifdef LL_WINDOWS
#include <windows.h> // Does not include winsock.h because WIN32_LEAN_AND_MEAN is defined
+#include <ws2tcpip.h>
#include <winsock2.h> // Requires windows.h
#endif
diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h
index b40a718593..78d5e50e4b 100644
--- a/indra/llcommon/stdtypes.h
+++ b/indra/llcommon/stdtypes.h
@@ -169,14 +169,14 @@ private:
FROM mValue;
public:
- narrow(FROM value): mValue(value) {}
+ constexpr narrow(FROM value): mValue(value) {}
/*---------------------- Narrowing unsigned to signed ----------------------*/
template <typename TO,
typename std::enable_if<std::is_unsigned<FROM>::value &&
std::is_signed<TO>::value,
bool>::type = true>
- inline
+ constexpr
operator TO() const
{
// The reason we skip the
@@ -194,7 +194,7 @@ public:
typename std::enable_if<! (std::is_unsigned<FROM>::value &&
std::is_signed<TO>::value),
bool>::type = true>
- inline
+ constexpr
operator TO() const
{
// two different assert()s so we can tell which condition failed
diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp
index 84eb41b5fe..2fea92e3b7 100644
--- a/indra/llcommon/tests/lldependencies_test.cpp
+++ b/indra/llcommon/tests/lldependencies_test.cpp
@@ -31,7 +31,6 @@
#include <string>
// std headers
// external library headers
-#include <boost/assign/list_of.hpp>
// Precompiled header
#include "linden_common.h"
// associated header
@@ -106,8 +105,6 @@ std::ostream& operator<<(std::ostream& out, const std::set<ENTRY>& set)
/*****************************************************************************
* Other helpers
*****************************************************************************/
-using boost::assign::list_of;
-
typedef LLDependencies<> StringDeps;
typedef StringDeps::KeyList StringList;
@@ -165,7 +162,7 @@ namespace tut
// The quick brown fox jumps over the lazy yellow dog.
// (note, "The" and "the" are distinct, else this test wouldn't work)
deps.add("lazy");
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")));
+ ensure_equals(sorted_keys(deps), StringList{"lazy"});
deps.add("jumps");
ensure("found lazy", deps.get("lazy"));
ensure("not found dog.", ! deps.get("dog."));
@@ -175,24 +172,23 @@ namespace tut
// A change to the implementation of boost::topological_sort() would
// be an acceptable reason, and you can simply update the expected
// test output.
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")));
- deps.add("The", 0, empty, list_of("fox")("dog."));
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps" });
+ deps.add("The", 0, empty, { "fox", "dog." });
// Test key accessors
ensure("empty before deps for missing key", is_empty(deps.get_before_range("bogus")));
ensure("empty before deps for jumps", is_empty(deps.get_before_range("jumps")));
- ensure_equals(instance_from_range< std::set<std::string> >(deps.get_before_range("The")),
- make< std::set<std::string> >(list_of("dog.")("fox")));
+ ensure_equals(instance_from_range< std::set<std::string> >(deps.get_before_range("The")), std::set<std::string>{ "dog.", "fox" });
// resume building dependencies
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The")));
- deps.add("the", 0, list_of("The"));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The")("the")));
- deps.add("fox", 0, list_of("The"), list_of("jumps"));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
- deps.add("the", 0, list_of("The")); // same, see if cache works
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
- deps.add("jumps", 0, empty, list_of("over")); // update jumps deps
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
-/*==========================================================================*|
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps", "The" });
+ deps.add("the", 0, { "The" });
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "jumps", "The", "the" });
+ deps.add("fox", 0, { "The" }, { "jumps" });
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" });
+ deps.add("the", 0, { "The" }); // same, see if cache works
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" });
+ deps.add("jumps", 0, empty, { "over" }); // update jumps deps
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps" });
+ /*==========================================================================*|
// It drives me nuts that this test doesn't work in the test
// framework, because -- for reasons unknown -- running the test
// framework on Mac OS X 10.5 Leopard and Windows XP Pro, the catch
@@ -216,22 +212,21 @@ namespace tut
deps.remove("over");
}
|*==========================================================================*/
- deps.add("dog.", 0, list_of("yellow")("lazy"));
- ensure_equals(instance_from_range< std::set<std::string> >(deps.get_after_range("dog.")),
- make< std::set<std::string> >(list_of("lazy")("yellow")));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")("dog.")));
- deps.add("quick", 0, list_of("The"), list_of("fox")("brown"));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("quick")("fox")("jumps")("dog.")));
- deps.add("over", 0, list_of("jumps"), list_of("yellow")("the"));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("quick")("fox")("jumps")("over")("the")("dog.")));
- deps.add("yellow", 0, list_of("the"), list_of("lazy"));
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("fox")("jumps")("over")("the")("yellow")("lazy")("dog.")));
+ deps.add("dog.", 0, { "yellow", "lazy" });
+ ensure_equals(instance_from_range< std::set<std::string> >(deps.get_after_range("dog.")), std::set<std::string>{ "lazy", "yellow" });
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "fox", "jumps", "dog." });
+ deps.add("quick", 0, { "The" }, { "fox", "brown" });
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "the", "quick", "fox", "jumps", "dog." });
+ deps.add("over", 0, { "jumps" }, { "yellow", "the" });
+ ensure_equals(sorted_keys(deps), StringList{ "lazy", "The", "quick", "fox", "jumps", "over", "the", "dog." });
+ deps.add("yellow", 0, { "the" }, { "lazy" });
+ ensure_equals(sorted_keys(deps), StringList{ "The", "quick", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." });
deps.add("brown");
// By now the dependencies are pretty well in place. A change to THIS
// order should be viewed with suspicion.
- ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("brown")("fox")("jumps")("over")("the")("yellow")("lazy")("dog.")));
+ ensure_equals(sorted_keys(deps), StringList{ "The", "quick", "brown", "fox", "jumps", "over", "the", "yellow", "lazy", "dog." });
- StringList keys(make<StringList>(list_of("The")("brown")("dog.")("fox")("jumps")("lazy")("over")("quick")("the")("yellow")));
+ StringList keys(StringList{ "The", "brown", "dog.", "fox", "jumps", "lazy", "over", "quick", "the", "yellow" });
ensure_equals(instance_from_range<StringList>(deps.get_key_range()), keys);
#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
// This is the succinct way, works on modern compilers
@@ -255,9 +250,9 @@ namespace tut
typedef LLDependencies<std::string, int> NameIndexDeps;
NameIndexDeps nideps;
const NameIndexDeps& const_nideps(nideps);
- nideps.add("def", 2, list_of("ghi"));
+ nideps.add("def", 2, { "ghi" });
nideps.add("ghi", 3);
- nideps.add("abc", 1, list_of("def"));
+ nideps.add("abc", 1, { "def" });
NameIndexDeps::range range(nideps.get_range());
ensure_equals(range.begin()->first, "abc");
ensure_equals(range.begin()->second, 1);
@@ -269,20 +264,20 @@ namespace tut
ensure_equals(const_iterator->first, "def");
ensure_equals(const_iterator->second, 2);
// NameIndexDeps::node_range node_range(nideps.get_node_range());
-// ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
+// ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1,2,3)));
// *node_range.begin() = 0;
// *node_range.begin() = 1;
NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range());
- ensure_equals(instance_from_range<std::vector<int> >(const_node_range), make< std::vector<int> >(list_of(1)(2)(3)));
+ ensure_equals(instance_from_range<std::vector<int>>(const_node_range), std::vector<int>{ 1, 2, 3 });
NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range());
- ensure_equals(instance_from_range<StringList>(const_key_range), make<StringList>(list_of("abc")("def")("ghi")));
+ ensure_equals(instance_from_range<StringList>(const_key_range), StringList{ "abc", "def", "ghi" });
NameIndexDeps::sorted_range sorted(const_nideps.sort());
NameIndexDeps::sorted_iterator sortiter(sorted.begin());
ensure_equals(sortiter->first, "ghi");
ensure_equals(sortiter->second, 3);
// test all iterator-flavored versions of get_after_range()
- StringList def(make<StringList>(list_of("def")));
+ StringList def{"def"};
ensure("empty abc before list", is_empty(nideps.get_before_range(nideps.get_range().begin())));
ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_range().begin())),
def);
@@ -296,7 +291,6 @@ namespace tut
def);
// advance from "ghi" to "def", which must come after "ghi"
++sortiter;
- ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(sortiter)),
- make<StringList>(list_of("ghi")));
+ ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(sortiter)), StringList{ "ghi" });
}
} // namespace tut
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index 44f772e322..8b206f7b14 100644
--- a/indra/llcommon/tests/lleventdispatcher_test.cpp
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -33,7 +33,6 @@
#include <stdexcept>
#include <boost/bind.hpp>
-#include <boost/function.hpp>
#include <boost/range.hpp>
#include <boost/lambda/lambda.hpp>
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index b63cc52bec..d3d8e54d45 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -21,7 +21,6 @@
// external library headers
#include "llapr.h"
#include "apr_thread_proc.h"
-#include <boost/function.hpp>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/finder.hpp>
// other Linden headers
@@ -54,14 +53,29 @@ std::string apr_strerror_helper(apr_status_t rv)
*****************************************************************************/
#define ensure_equals_(left, right) \
- ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
+do { \
+ auto _left_val = (left); \
+ auto _right_val = (right); \
+ if (_left_val != _right_val) { \
+ std::string _msg = std::string(#left) + " != " + std::string(#right); \
+ tut::ensure_equals(_msg, _left_val, _right_val); \
+ } else { \
+ tut::ensure_equals("", _left_val, _right_val); \
+ } \
+} while(0)
#define aprchk(expr) aprchk_(#expr, (expr))
static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
{
- tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr_strerror_helper
- (rv)),
- rv, expected);
+ if (rv != expected)
+ {
+ std::string msg = std::string(call) + " => " + std::to_string(rv) + ": " + apr_strerror_helper(rv);
+ tut::ensure_equals(msg, rv, expected);
+ }
+ else
+ {
+ tut::ensure_equals("", rv, expected);
+ }
}
/**
@@ -78,11 +92,14 @@ static std::string readfile(const std::string& pathname, const std::string& desc
std::string use_desc(desc);
if (use_desc.empty())
{
- use_desc = STRINGIZE("in " << pathname);
+ use_desc = "in " + pathname;
}
std::ifstream inf(pathname.c_str());
std::string output;
- tut::ensure(STRINGIZE("No output " << use_desc), bool(std::getline(inf, output)));
+ if (!std::getline(inf, output))
+ {
+ tut::ensure("No output " + use_desc, false);
+ }
std::string more;
while (std::getline(inf, more))
{
@@ -108,8 +125,10 @@ void waitfor(LLProcess& proc, int timeout=60)
{
yield();
}
- tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
- i < timeout);
+ // Pump once more after the process exits to flush any final events such as EOF.
+ yield(0);
+ std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
+ tut::ensure(msg, i < timeout);
}
void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
@@ -119,8 +138,10 @@ void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
{
yield();
}
- tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
- i < timeout);
+ // Pump once more after the process exits to flush any final events such as EOF.
+ yield(0);
+ std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
+ tut::ensure(msg, i < timeout);
}
/**
@@ -153,7 +174,8 @@ struct PythonProcessLauncher
try
{
mPy = LLProcess::create(mParams);
- tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ std::string msg = "Couldn't launch " + mDesc + " script";
+ tut::ensure(msg, bool(mPy));
}
catch (const tut::failure&)
{
@@ -214,7 +236,8 @@ struct PythonProcessLauncher
mParams.args.add(out.getName());
run();
// assuming the script wrote to that file, read it
- return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
+ std::string desc = "from " + mDesc + " script";
+ return readfile(out.getName(), desc);
}
LLProcess::Params mParams;
@@ -240,9 +263,12 @@ static std::string python_out(const std::string& desc, const CONTENT& script)
}
/// Create a temporary directory and clean it up later.
-class NamedTempDir: public boost::noncopyable
+class NamedTempDir
{
public:
+ NamedTempDir(const NamedTempDir&) = delete;
+ NamedTempDir& operator=(const NamedTempDir&) = delete;
+
NamedTempDir():
mPath(NamedTempFile::temp_path()),
mCreated(boost::filesystem::create_directories(mPath))
@@ -1071,8 +1097,11 @@ namespace tut
ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0);
}
- struct EventListener: public boost::noncopyable
+ struct EventListener
{
+ EventListener(const EventListener&) = delete;
+ EventListener& operator=(const EventListener&) = delete;
+
EventListener(LLEventPump& pump)
{
mConnection =
@@ -1183,8 +1212,8 @@ namespace tut
{
set_test_name("ReadPipe \"eof\" event");
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello from Python!')\n");
+ "import time\n"
+ "time.sleep(1.5)\n");
py.mParams.files.add(LLProcess::FileParam()); // stdin
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
py.launch();
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index b68c63a15f..6a2c967363 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -28,13 +28,10 @@
#include "linden_common.h"
-#include <boost/assign/list_of.hpp>
#include "../llstring.h"
#include "StringVec.h" // must come BEFORE lltut.h
#include "../test/lltut.h"
-using boost::assign::list_of;
-
namespace tut
{
struct string_index
@@ -763,14 +760,14 @@ namespace tut
ensure_equals("only delims",
LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
ensure_equals("sequence of delims",
- LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
+ LLStringUtil::getTokens(",,, one ,,,", ","), StringVec{"one"});
// nat considers this a dubious implementation side effect, but I'd
// hate to change it now...
ensure_equals("noncontiguous tokens",
- LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
+ LLStringUtil::getTokens(", ,, , one ,,,", ","), StringVec{ "", "", "one" });
ensure_equals("space-padded tokens",
- LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
- ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
+ LLStringUtil::getTokens(", one , two ,", ","), StringVec{"one", "two"});
+ ensure_equals("no delims", LLStringUtil::getTokens("one", ","), StringVec{ "one" });
}
// Shorthand for verifying that getTokens() behaves the same when you
@@ -817,39 +814,33 @@ namespace tut
ensure_getTokens("only delims",
" \r\n ", " \r\n", "", StringVec());
ensure_getTokens("sequence of delims",
- ",,, one ,,,", ", ", "", list_of("one"));
+ ",,, one ,,,", ", ", "", StringVec{"one"});
// Note contrast with the case in the previous method
ensure_getTokens("noncontiguous tokens",
- ", ,, , one ,,,", ", ", "", list_of("one"));
+ ", ,, , one ,,,", ", ", "", StringVec{"one"});
ensure_getTokens("space-padded tokens",
", one , two ,", ", ", "",
- list_of("one")("two"));
- ensure_getTokens("no delims", "one", ",", "", list_of("one"));
+ StringVec{"one", "two"});
+ ensure_getTokens("no delims", "one", ",", "", StringVec{ "one" });
// drop_delims vs. keep_delims
ensure_getTokens("arithmetic",
- " ab+def / xx* yy ", " ", "+-*/",
- list_of("ab")("+")("def")("/")("xx")("*")("yy"));
+ " ab+def / xx* yy ", " ", "+-*/", { "ab", "+", "def", "/", "xx", "*", "yy" });
// quotes
ensure_getTokens("no quotes",
- "She said, \"Don't go.\"", " ", ",", "",
- list_of("She")("said")(",")("\"Don't")("go.\""));
+ "She said, \"Don't go.\"", " ", ",", "", { "She", "said", ",", "\"Don't", "go.\"" });
ensure_getTokens("quotes",
- "She said, \"Don't go.\"", " ", ",", "\"",
- list_of("She")("said")(",")("Don't go."));
+ "She said, \"Don't go.\"", " ", ",", "\"", { "She", "said", ",", "Don't go." });
ensure_getTokens("quotes and delims",
"run c:/'Documents and Settings'/someone", " ", "", "'",
- list_of("run")("c:/Documents and Settings/someone"));
+ { "run", "c:/Documents and Settings/someone" });
ensure_getTokens("unmatched quote",
- "baby don't leave", " ", "", "'",
- list_of("baby")("don't")("leave"));
+ "baby don't leave", " ", "", "'", { "baby", "don't", "leave" });
ensure_getTokens("adjacent quoted",
- "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
- list_of("abcdef \"ghijkl' mnopqr"));
+ "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'", { "abcdef \"ghijkl' mnopqr" });
ensure_getTokens("quoted empty string",
- "--set SomeVar ''", " ", "", "'",
- list_of("--set")("SomeVar")(""));
+ "--set SomeVar ''", " ", "", "'", { "--set", "SomeVar", "" });
// escapes
// Don't use backslash as an escape for these tests -- you'll go nuts
@@ -857,15 +848,12 @@ namespace tut
// something else!
ensure_equals("escaped delims",
LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
- list_of(" a")("-")("dog-gone phrase"));
+ StringVec{ " a", "-", "dog-gone phrase" });
ensure_equals("escaped quotes",
LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
- list_of("say:")("this isn't working."));
+ StringVec{ "say:", "this isn't working." });
ensure_equals("escaped escape",
- LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
- list_of("want")("x^2"));
- ensure_equals("escape at end",
- LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
- list_of("it's up")("there^"));
+ LLStringUtil::getTokens("want x^^2", " ", "", "", "^"), StringVec{ "want", "x^2" });
+ ensure_equals("escape at end", LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"), StringVec{ "it's up", "there^" });
}
}
diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp
index 7a2adfd8ba..6734596d25 100644
--- a/indra/llcommon/tests/lltreeiterators_test.cpp
+++ b/indra/llcommon/tests/lltreeiterators_test.cpp
@@ -32,6 +32,7 @@
// STL headers
// std headers
+#include <functional>
#include <iostream>
#include <sstream>
#include <string>
@@ -915,7 +916,7 @@ struct WalkExpected<LLTreeIter::BFS>: public Expected
template <class NODE, typename CHILDITER>
typename LLPtrTo<NODE>::type
get_B2b(const typename LLPtrTo<NODE>::type& root,
- const boost::function<CHILDITER(const typename LLPtrTo<NODE>::type&)>& child_begin)
+ const std::function<CHILDITER(const typename LLPtrTo<NODE>::type&)>& child_begin)
{
typedef typename LLPtrTo<NODE>::type NodePtr;
CHILDITER Bi(child_begin(root));
diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp
index a3ce7f8e3d..3f99041592 100644
--- a/indra/llcommon/tests/stringize_test.cpp
+++ b/indra/llcommon/tests/stringize_test.cpp
@@ -110,6 +110,17 @@ namespace tut
void stringize_object::test<3>()
{
//Tests rely on validity of wstring_to_utf8str()
+#if LL_WINDOWS // Windows wstring is a 2byte UTF16 type
+ ensure_equals(ll_convert<std::string>(wstringize(c)), ll_convert<std::string>(std::wstring(L"c")));
+ ensure_equals(ll_convert<std::string>(wstringize(s)), ll_convert<std::string>(std::wstring(L"17")));
+ ensure_equals(ll_convert<std::string>(wstringize(i)), ll_convert<std::string>(std::wstring(L"34")));
+ ensure_equals(ll_convert<std::string>(wstringize(l)), ll_convert<std::string>(std::wstring(L"68")));
+ ensure_equals(ll_convert<std::string>(wstringize(f)), ll_convert<std::string>(std::wstring(L"3.14159")));
+ ensure_equals(ll_convert<std::string>(wstringize(d)), ll_convert<std::string>(std::wstring(L"3.14159")));
+ ensure_equals(ll_convert<std::string>(wstringize(abc)), ll_convert<std::string>(std::wstring(L"abc def")));
+ ensure_equals(ll_convert<std::string>(wstringize(abc)), ll_convert<std::string>(wstringize(abc.c_str())));
+ ensure_equals(ll_convert<std::string>(wstringize(def)), ll_convert<std::string>(std::wstring(L"def ghi")));
+#else
ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
@@ -119,6 +130,7 @@ namespace tut
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
+#endif
// ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
}
} // namespace tut
diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h
index 0eb1891754..ac4f415f3e 100644
--- a/indra/llcommon/threadpool.h
+++ b/indra/llcommon/threadpool.h
@@ -55,7 +55,7 @@ namespace LL
* ThreadPool listens for application shutdown messages on the "LLApp"
* LLEventPump. Call close() to shut down this ThreadPool early.
*/
- virtual void close();
+ void close();
std::string getName() const { return mName; }
size_t getWidth() const { return mThreads.size(); }
@@ -122,7 +122,7 @@ namespace LL
size_t threads=1,
size_t capacity=1024*1024,
bool auto_shutdown = true):
- ThreadPoolBase(name, threads, new queue_t(name, capacity), auto_shutdown)
+ ThreadPoolBase(name, threads, new queue_t(name, capacity, false), auto_shutdown)
{}
~ThreadPoolUsing() override {}
diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp
index 6066e74fb5..0407d6c3e9 100644
--- a/indra/llcommon/workqueue.cpp
+++ b/indra/llcommon/workqueue.cpp
@@ -17,9 +17,11 @@
// std headers
// external library headers
// other Linden headers
+#include "llapp.h"
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include "llerror.h"
+#include "llevents.h"
#include "llexception.h"
#include "stringize.h"
@@ -29,11 +31,38 @@ using Lock = LLCoros::LockType;
/*****************************************************************************
* WorkQueueBase
*****************************************************************************/
-LL::WorkQueueBase::WorkQueueBase(const std::string& name):
- super(makeName(name))
+LL::WorkQueueBase::WorkQueueBase(const std::string& name, bool auto_shutdown)
+ : super(makeName(name))
{
- // TODO: register for "LLApp" events so we can implicitly close() on
- // viewer shutdown.
+ if (auto_shutdown)
+ {
+ // Register for "LLApp" events so we can implicitly close() on viewer shutdown
+ std::string listener_name = "WorkQueue:" + getKey();
+ LLEventPumps::instance().obtain("LLApp").listen(
+ listener_name,
+ [this](const LLSD& stat)
+ {
+ std::string status(stat["status"]);
+ if (status != "running")
+ {
+ // Viewer is shutting down, close this queue
+ LL_DEBUGS("WorkQueue") << getKey() << " closing on app shutdown" << LL_ENDL;
+ close();
+ }
+ return false;
+ });
+
+ // Store the listener name so we can unregister in the destructor
+ mListenerName = listener_name;
+ }
+}
+
+LL::WorkQueueBase::~WorkQueueBase()
+{
+ if (!mListenerName.empty() && !LLEventPumps::wasDeleted())
+ {
+ LLEventPumps::instance().obtain("LLApp").stopListening(mListenerName);
+ }
}
void LL::WorkQueueBase::runUntilClose()
@@ -102,19 +131,108 @@ std::string LL::WorkQueueBase::makeName(const std::string& name)
return STRINGIZE("WorkQueue" << num);
}
+namespace
+{
+#if LL_WINDOWS
+
+ static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+ U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
+ {
+ if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
+ {
+ // Handled
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else
+ {
+ // handle it, convert to std::exception
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ void cpphandle(const LL::WorkQueueBase::Work& work)
+ {
+ // SE and C++ can not coexists, thus two handlers
+ try
+ {
+ work();
+ }
+ catch (const LLContinueError&)
+ {
+ // Any uncaught exception derived from LLContinueError will be caught
+ // here and logged. This coroutine will terminate but the rest of the
+ // viewer will carry on.
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("LLContinue in work queue"));
+ }
+ }
+
+ void sehandle(const LL::WorkQueueBase::Work& work)
+ {
+ __try
+ {
+ // handle stop and continue exceptions first
+ cpphandle(work);
+ }
+ __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // convert to C++ styled exception
+ char integer_string[512];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+ }
+#endif // LL_WINDOWS
+} // anonymous namespace
+
void LL::WorkQueueBase::callWork(const Work& work)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
+#ifdef LL_WINDOWS
+ // can not use __try directly, toplevel requires unwinding, thus use of a wrapper
+ sehandle(work);
+#else // LL_WINDOWS
try
{
work();
}
- catch (...)
+ catch (LLContinueError&)
{
- // No matter what goes wrong with any individual work item, the worker
- // thread must go on! Log our own instance name with the exception.
LOG_UNHANDLED_EXCEPTION(getKey());
}
+ catch (...)
+ {
+ if (getKey() != "mainloop")
+ {
+ // Stash any other kind of uncaught exception to be rethrown by main thread.
+ LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
+ << getKey() << LL_ENDL;
+
+ std::string name = getKey();
+ LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
+ main_queue->post(
+ // Bind the current exception, rethrow it in main loop.
+ [exc = std::current_exception(), name]()
+ {
+ LL_INFOS("LLCoros") << "Rethrowing exception from WorkQueueBase::callWork " << name << LL_ENDL;
+ std::rethrow_exception(exc);
+ });
+ }
+ else
+ {
+ // let main loop crash
+ throw;
+ }
+ }
+#endif // else LL_WINDOWS
}
void LL::WorkQueueBase::error(const std::string& msg)
@@ -135,8 +253,8 @@ void LL::WorkQueueBase::checkCoroutine(const std::string& method)
/*****************************************************************************
* WorkQueue
*****************************************************************************/
-LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity):
- super(name),
+LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity, bool auto_shutdown):
+ super(name, auto_shutdown),
mQueue(capacity)
{
}
@@ -184,8 +302,8 @@ bool LL::WorkQueue::tryPop_(Work& work)
/*****************************************************************************
* WorkSchedule
*****************************************************************************/
-LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity):
- super(name),
+LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity, bool auto_shutdown):
+ super(name, auto_shutdown),
mQueue(capacity)
{
}
diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h
index 9d7bbfbf7a..573203a5b3 100644
--- a/indra/llcommon/workqueue.h
+++ b/indra/llcommon/workqueue.h
@@ -51,7 +51,9 @@ namespace LL
* You may omit the WorkQueueBase name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
- WorkQueueBase(const std::string& name);
+ WorkQueueBase(const std::string& name, bool auto_shutdown);
+
+ virtual ~WorkQueueBase();
/**
* Since the point of WorkQueue is to pass work to some other worker
@@ -197,6 +199,9 @@ namespace LL
private:
virtual Work pop_() = 0;
virtual bool tryPop_(Work&) = 0;
+
+ // Name used for the LLApp event listener (empty if not registered)
+ std::string mListenerName;
};
/*****************************************************************************
@@ -212,7 +217,7 @@ namespace LL
* You may omit the WorkQueue name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
- WorkQueue(const std::string& name = std::string(), size_t capacity=1024);
+ WorkQueue(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true);
/**
* Since the point of WorkQueue is to pass work to some other worker
@@ -282,7 +287,7 @@ namespace LL
* You may omit the WorkSchedule name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
- WorkSchedule(const std::string& name = std::string(), size_t capacity=1024);
+ WorkSchedule(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true);
/**
* Since the point of WorkSchedule is to pass work to some other worker
@@ -525,7 +530,11 @@ namespace LL
reply,
// Bind the current exception to transport back to the
// originating WorkQueue. Once there, rethrow it.
- [exc = std::current_exception()](){ std::rethrow_exception(exc); });
+ [exc = std::current_exception()]()
+ {
+ LL_INFOS("LLCoros") << "Rethrowing exception from WorkQueueBase::postTo" << LL_ENDL;
+ std::rethrow_exception(exc);
+ });
}
},
// if caller passed a TimePoint, pass it along to post()