diff options
Diffstat (limited to 'indra/llcommon')
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() |
