summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-11-25 22:34:59 +0200
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-11-25 22:34:59 +0200
commita58abdac5fd642ee320345bd1eef0df253895dba (patch)
treec71007ccb2746b7bef7949c2c978cbfe768392ce /indra/llcommon
parent624eaf59a02dbd2f002fb45af01bbc17f04f8331 (diff)
parent9fe788e031e83aa6bfbb7bc9144079d2814018e8 (diff)
Merge branch develop into project/fonts-update
# Conflicts: # indra/llrender/llfontfreetype.cpp
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt3
-rw-r--r--indra/llcommon/fsyspath.h47
-rw-r--r--indra/llcommon/indra_constants.h386
-rw-r--r--indra/llcommon/linden_common.h6
-rw-r--r--indra/llcommon/llapp.cpp4
-rw-r--r--indra/llcommon/llapp.h2
-rw-r--r--indra/llcommon/llassettype.cpp17
-rw-r--r--indra/llcommon/llassettype.h2
-rw-r--r--indra/llcommon/llcommon.cpp72
-rw-r--r--indra/llcommon/llcoros.cpp57
-rw-r--r--indra/llcommon/lldate.cpp9
-rw-r--r--indra/llcommon/lldate.h1
-rw-r--r--indra/llcommon/lldefs.h14
-rw-r--r--indra/llcommon/llerror.cpp2
-rw-r--r--indra/llcommon/llfasttimer.cpp2
-rw-r--r--indra/llcommon/llfasttimer.h62
-rw-r--r--indra/llcommon/llfile.cpp410
-rw-r--r--indra/llcommon/llfile.h108
-rw-r--r--indra/llcommon/llleap.cpp11
-rw-r--r--indra/llcommon/llmd5.cpp9
-rw-r--r--indra/llcommon/llmemory.h57
-rw-r--r--indra/llcommon/llpreprocessor.h36
-rw-r--r--indra/llcommon/llprocess.cpp3
-rw-r--r--indra/llcommon/llprocessor.cpp47
-rw-r--r--indra/llcommon/llprocessor.h15
-rw-r--r--indra/llcommon/llprofiler.h32
-rw-r--r--indra/llcommon/llsdutil.h94
-rw-r--r--indra/llcommon/llsys.cpp17
-rw-r--r--indra/llcommon/llthread.cpp105
-rw-r--r--indra/llcommon/llthread.h7
-rw-r--r--indra/llcommon/llthreadsafequeue.h4
-rw-r--r--indra/llcommon/lluuid.cpp15
-rw-r--r--indra/llcommon/lluuid.h2
-rw-r--r--indra/llcommon/stdtypes.h6
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp52
-rw-r--r--indra/llcommon/tests/stringize_test.cpp12
-rw-r--r--indra/llcommon/threadpool.h4
-rw-r--r--indra/llcommon/workqueue.cpp135
-rw-r--r--indra/llcommon/workqueue.h11
39 files changed, 1245 insertions, 633 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index a504e71340..2de9deea70 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -11,7 +11,7 @@ include(LLSharedLibs)
include(Copy3rdPartyLibs)
include(ZLIBNG)
include(Tracy)
-
+include(SSE2NEON)
set(llcommon_SOURCE_FILES
apply.cpp
@@ -277,6 +277,7 @@ target_link_libraries(
ll::boost
ll::oslibraries
ll::tracy
+ ll::sse2neon
)
target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
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/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/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/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 1539b48bd3..9e95d9c85f 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -310,26 +310,55 @@ namespace
static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
+U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
{
- if (code == STATUS_MSC_EXCEPTION)
+ 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
+ // handle it, convert to std::exception
return EXCEPTION_EXECUTE_HANDLER;
}
+
+ return EXCEPTION_CONTINUE_SEARCH;
}
-void sehandle(const LLCoros::callable_t& callable)
+void cpphandle(const LLCoros::callable_t& callable, const std::string& name)
{
- __try
+ // 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
@@ -340,16 +369,7 @@ void sehandle(const LLCoros::callable_t& callable)
throw std::exception(integer_string);
}
}
-
-#else // ! LL_WINDOWS
-
-inline void sehandle(const LLCoros::callable_t& callable)
-{
- callable();
-}
-
-#endif // ! LL_WINDOWS
-
+#endif // LL_WINDOWS
} // anonymous namespace
// Top-level wrapper around caller's coroutine callable.
@@ -362,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
{
- sehandle(callable);
+ callable();
}
catch (const Stop& exc)
{
@@ -387,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/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/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/llerror.cpp b/indra/llcommon/llerror.cpp
index d834098994..3411e9c6bb 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"));
}
}
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index 722743f453..1c5fe9d2f5 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 09fcf8a1af..271dade097 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,35 +73,10 @@ 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()
{
unsigned __int64 val = __rdtsc();
@@ -159,23 +139,37 @@ public:
#endif // (LL_LINUX) && !(defined(__i386__) || defined(__amd64__))
-#if (LL_LINUX || LL_DARWIN) && (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) && (LL_X86 || LL_X86_64)
//
// 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/llleap.cpp b/indra/llcommon/llleap.cpp
index 662a2511cd..ada6b9519e 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -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/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 b616edfde7..adc556d180 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -71,7 +71,11 @@ LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
#define ll_assert_aligned(ptr,alignment)
#endif
+#if LL_ARM64
+#include "sse2neon.h"
+#else
#include <xmmintrin.h>
+#endif
template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address)
{
@@ -231,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))
@@ -248,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
@@ -261,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);
@@ -285,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);
}
@@ -310,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);
@@ -375,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/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index b499a9ce10..b2a9734548 100644
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -76,6 +76,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
// Different name, same functionality.
@@ -118,11 +135,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
@@ -167,7 +181,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
@@ -187,4 +201,16 @@
#define LL_PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif
+#if LL_ARM64
+#define GLM_FORCE_NEON 1
+#else
+#define GLM_FORCE_SSE2 1
+#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 912e596c3f..33a9180cc6 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;
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index a783e18e49..0778b123ea 100644
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -628,6 +628,8 @@ private:
#elif LL_DARWIN
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
#include <mach/machine.h>
#include <sys/sysctl.h>
@@ -638,17 +640,21 @@ public:
{
getCPUIDInfo();
uint64_t frequency = getSysctlInt64("hw.cpufrequency");
+ if (frequency == 0) // fallback to clockrate and tbfrequency
+ {
+ frequency = getSysctlClockrate() * getSysctlInt64("hw.tbfrequency");
+ }
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;
}
@@ -656,7 +662,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 ) )
@@ -676,6 +682,14 @@ private:
return result == -1 ? 0 : value;
}
+ uint64_t getSysctlClockrate()
+ {
+ struct clockinfo clockrate{};
+ size_t size = sizeof(clockrate);
+ int error = sysctlbyname("kern.clockrate", &clockrate, &size, nullptr, 0);
+ return error == -1 ? 0 : clockrate.hz;
+ }
+
void getCPUIDInfo()
{
size_t len = 0;
@@ -725,32 +739,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
-
- 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 // 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);
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/llprofiler.h b/indra/llcommon/llprofiler.h
index f6a4d24747..5fb32d6280 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -74,23 +74,18 @@
#define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER
#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
@@ -158,23 +153,20 @@ extern thread_local bool gProfilerEnabled;
#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 +174,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/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/llsys.cpp b/indra/llcommon/llsys.cpp
index 3f33ad61c5..270ca40086 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -192,6 +192,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));
@@ -210,6 +213,8 @@ LLOSInfo::LLOSInfo() :
}
}
+#pragma warning(pop)
+
S32 ubr = 0; // Windows 10 Update Build Revision, can be retrieved from a registry
if (mMajorVer == 10)
{
@@ -1323,7 +1328,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");
@@ -1343,10 +1348,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:
@@ -1367,7 +1368,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");
@@ -1394,10 +1395,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..692941a892 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,69 @@ 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()]() { 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 034e3f7897..1a1d06a6fd 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -452,7 +452,9 @@ ElementT LLThreadSafeQueue<ElementT, QueueT>::pop(void)
// so we can finish draining the queue.
pop_result popped = pop_(lock1, value);
if (popped == POPPED)
- return std::move(value);
+ // don't use std::move when returning local value because
+ // it prevents the compiler from optimizing with copy elision
+ return value;
// Once the queue is DONE, there will never be any more coming.
if (popped == DONE)
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index b9bd27aa17..4aae90626e 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/stdtypes.h b/indra/llcommon/stdtypes.h
index 28e50b3d21..f1e4c2bc78 100644
--- a/indra/llcommon/stdtypes.h
+++ b/indra/llcommon/stdtypes.h
@@ -164,14 +164,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
@@ -189,7 +189,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/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index b63cc52bec..4adfaf54f3 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -54,14 +54,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 +93,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 +126,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 +139,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 +175,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 +237,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;
@@ -1183,8 +1207,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/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..7efaebd569 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,103 @@ 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;
+
+ 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()]() { std::rethrow_exception(exc); });
+ }
+ else
+ {
+ // let main loop crash
+ throw;
+ }
+ }
+#endif // else LL_WINDOWS
}
void LL::WorkQueueBase::error(const std::string& msg)
@@ -135,8 +248,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 +297,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..735ad38a26 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