Merge branch 'DRTVWR-559' into marchcat/587-v-pbr-merge

# Conflicts:
#	indra/llcommon/CMakeLists.txt
#	indra/newview/llspatialpartition.cpp
#	indra/newview/llviewergenericmessage.cpp
#	indra/newview/llvoavatar.cpp
master
Andrey Lihatskiy 2023-11-29 10:45:43 +02:00
commit d1c0a5b840
800 changed files with 47650 additions and 42389 deletions

13
.gitignore vendored
View File

@ -7,9 +7,18 @@
*.pyc
*.rej
*.swp
*.vcxproj
*.filters
*.sln
*.depend
*.stamp
*.rc
*~
# Specific paths and/or names
CMakeCache.txt
cmake_install.cmake
LICENSES
build-darwin-*
build-linux-*
@ -17,6 +26,10 @@ debian/files
debian/secondlife-appearance-utility*
debian/secondlife-viewer*
indra/.distcc
indra/cmake/*
indra/out/*
indra/packages/*
build-vc80/
build-vc100/
build-vc120/

View File

@ -702,11 +702,11 @@
<key>archive</key>
<map>
<key>hash</key>
<string>34af0a90a3015b7e7ec2486090bc4ce6ee5be758</string>
<string>7cc58b3acb230a7e65ea5f0ff800be393eb4aa1b</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-glext/releases/download/v68-af397ee/glext-68-common-af397ee.tar.zst</string>
<string>https://github.com/secondlife/3p-glext/releases/download/v69/glext-68-common-685b36e.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
@ -1732,6 +1732,62 @@
<key>description</key>
<string>Meshoptimizer. Mesh optimization library.</string>
</map>
<key>mikktspace</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-mikktspace</string>
<key>copyright</key>
<string>Copyright (C) 2011 by Morten S. Mikkelsen</string>
<key>description</key>
<string>Mikktspace Tangent Generator</string>
<key>license</key>
<string>Copyright (C) 2011 by Morten S. Mikkelsen</string>
<key>license_file</key>
<string>mikktspace.txt</string>
<key>name</key>
<string>mikktspace</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>b48b7ac0792d3ea8f087d99d9e4a29d8</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104415/914944/mikktspace-1-darwin64-574859.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>0a016b9c0c1e2c0b557e0124094da6c5</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104407/914918/mikktspace-1-windows-574859.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>02e9e5b6fe6788f4d2babb83ec544843</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104406/914909/mikktspace-1-windows64-574859.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>1</string>
</map>
<key>minizip-ng</key>
<map>
<key>platforms</key>
@ -2318,6 +2374,42 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>name</key>
<string>threejs</string>
</map>
<key>tinygltf</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-tinygltf</string>
<key>copyright</key>
<string>// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many contributors.</string>
<key>description</key>
<string>tinygltf import library</string>
<key>license</key>
<string>MIT</string>
<key>license_file</key>
<string>LICENSES/tinygltf_license.txt</string>
<key>name</key>
<string>tinygltf</string>
<key>platforms</key>
<map>
<key>common</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>4dad1c0948141e1667c01a3ee755e4dc</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105849/926137/tinygltf-v2.5.0-common-575729.tar.bz2</string>
</map>
<key>name</key>
<string>common</string>
</map>
</map>
<key>source</key>
<string>https://bitbucket.org/lindenlab/3p-tinygltf</string>
<key>source_type</key>
<string>git</string>
<key>version</key>
<string>v2.5.0</string>
</map>
<key>tracy</key>
<map>
<key>platforms</key>
@ -2327,11 +2419,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>b84ccb1606b3fc5b216d0123a23a4922e02b6bd8</string>
<key>hash_algorithm</key>
<string>sha1</string>
<string>9b6e1a1f4b0969d38a1ca8ee00aeb548</string>
<key>url</key>
<string>https://github.com/secondlife/3p-tracy/releases/download/v0.8.1-235e98f/tracy-v0.8.1.235e98f-darwin64-235e98f.tar.zst</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/110584/960613/tracy-v0.8.1.578241-darwin64-578241.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
@ -2341,11 +2431,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>54f126b85f179362cf0b6024e3cd621b53d68703</string>
<string>05b72ae5d733aed7d3bf142287601cc6</string>
<key>hash_algorithm</key>
<string>sha1</string>
<string>md5</string>
<key>url</key>
<string>https://github.com/secondlife/3p-tracy/releases/download/v0.8.1-235e98f/tracy-v0.8.1.235e98f-windows64-235e98f.tar.zst</string>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/110586/960637/tracy-v0.8.1.578241-windows64-578241.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
@ -2369,6 +2459,8 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>https://bitbucket.org/lindenlab/3p-tracy</string>
<key>source_type</key>
<string>git</string>
<key>version</key>
<string>v0.8.1.578241</string>
</map>
<key>tut</key>
<map>
@ -2630,6 +2722,62 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>description</key>
<string>XMLRPC Library</string>
</map>
<key>vulkan_gltf</key>
<map>
<key>canonical_repo</key>
<string>https://bitbucket.org/lindenlab/3p-vulkan-gltf-pbr</string>
<key>copyright</key>
<string>Copyright (c) 2018 Sascha Willems</string>
<key>description</key>
<string>Vulkan GLTF Sample Implementation</string>
<key>license</key>
<string>Copyright (c) 2018 Sascha Willems</string>
<key>license_file</key>
<string>LICENSES/vulkan_gltf.txt</string>
<key>name</key>
<string>vulkan_gltf</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>8cff2060843db3db788511ee34a8e8cc</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/101316/891509/vulkan_gltf-1-darwin64-572743.tar.bz2</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>58eea384be49ba756ce9c5e66669540b</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/101318/891520/vulkan_gltf-1-windows-572743.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>79b6a11622c2f83cfc2b7cd1fafb867b</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/101319/891521/vulkan_gltf-1-windows64-572743.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
<key>version</key>
<string>1</string>
</map>
<key>xxhash</key>
<map>
<key>platforms</key>
@ -2887,6 +3035,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>RelWithDebInfo</string>
<string>-project</string>
<string>SecondLife.xcodeproj</string>
<string>-parallelizeTargets</string>
</array>
</map>
<key>default</key>
@ -2914,6 +3063,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>RelWithDebInfo</string>
<string>-project</string>
<string>SecondLife.xcodeproj</string>
<string>-parallelizeTargets</string>
</array>
</map>
<key>name</key>
@ -2943,6 +3093,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>Release</string>
<string>-project</string>
<string>SecondLife.xcodeproj</string>
<string>-parallelizeTargets</string>
</array>
</map>
<key>name</key>
@ -2968,6 +3119,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>Release</string>
<string>-project</string>
<string>SecondLife.xcodeproj</string>
<string>-parallelizeTargets</string>
</array>
</map>
<key>name</key>

View File

@ -19,6 +19,7 @@ Agathos Frascati
CT-317
CT-352
Ai Austin
SL-19399
Aiko Ying
Aimee Trescothick
SNOW-227
@ -1421,6 +1422,7 @@ Sovereign Engineer
SL-18497
SL-18525
SL-18534
SL-19690
SL-19336
SpacedOut Frye
VWR-34

View File

@ -187,3 +187,4 @@ if (LINUX OR DARWIN)
endif (LINUX OR DARWIN)

View File

@ -64,6 +64,7 @@ set(cmake_SOURCE_FILES
VisualLeakDetector.cmake
LibVLCPlugin.cmake
XmlRpcEpi.cmake
xxHash.cmake
ZLIBNG.cmake
)

View File

@ -2,7 +2,7 @@
include_guard()
# FMODSTUDIO can be set when launching the make using the argument -DFMODSTUDIO:BOOL=ON
# FMODSTUDIO can be set when launching the make using the argument -DUSE_FMODSTUDIO:BOOL=ON
# When building using proprietary binaries though (i.e. having access to LL private servers),
# we always build with FMODSTUDIO.
if (INSTALL_PROPRIETARY)

View File

@ -3,9 +3,7 @@ include(Prebuilt)
include(GLH)
add_library( ll::glext INTERFACE IMPORTED )
if (WINDOWS OR LINUX)
use_system_binary(glext)
use_prebuilt_binary(glext)
endif (WINDOWS OR LINUX)
use_system_binary(glext)
use_prebuilt_binary(glext)

View File

@ -126,6 +126,13 @@ MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
message("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}")
endif()
if (DARWIN)
# test binaries always need to be signed for local development
set_target_properties(PROJECT_${project}_TEST_${name}
PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
endif ()
#
# Setup test targets
#
@ -221,6 +228,13 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
)
endif ()
if (DARWIN)
# test binaries always need to be signed for local development
set_target_properties(INTEGRATION_TEST_${testname}
PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-")
endif ()
# Add link deps to the executable
if(TEST_DEBUG)
message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})")

View File

@ -1,2 +1,5 @@
# -*- cmake -*-
include(Variables)
include(Mikktspace)

View File

@ -0,0 +1,6 @@
# -*- cmake -*-
include(Prebuilt)
if (NOT USESYSTEMLIBS)
use_prebuilt_binary(mikktspace)
endif (NOT USESYSTEMLIBS)

View File

@ -0,0 +1,7 @@
# -*- cmake -*-
include(Prebuilt)
use_prebuilt_binary(tinygltf)
set(TINYGLTF_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tinygltf)

View File

@ -11,8 +11,9 @@ if (USE_TRACY)
use_prebuilt_binary(tracy)
target_include_directories( ll::tracy SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/tracy)
target_link_libraries( ll::tracy INTERFACE TracyClient )
# See: indra/llcommon/llprofiler.h
target_compile_definitions(ll::tracy INTERFACE LL_PROFILER_CONFIGURATION=3 )
# See: indra/llcommon/llprofiler.h
add_compile_definitions(LL_PROFILER_CONFIGURATION=3)
endif (USE_TRACY)

View File

@ -33,7 +33,7 @@ set(LIBS_OPEN_PREFIX)
set(SCRIPTS_PREFIX ../scripts)
set(VIEWER_PREFIX)
set(INTEGRATION_TESTS_PREFIX)
set(LL_TESTS ON CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation")
set(LL_TESTS OFF CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation")
set(INCREMENTAL_LINK OFF CACHE BOOL "Use incremental linking on win32 builds (enable for faster links on some machines)")
set(ENABLE_MEDIA_PLUGINS ON CACHE BOOL "Turn off building media plugins if they are imported by third-party library mechanism")
set(VIEWER_SYMBOL_FILE "" CACHE STRING "Name of tarball into which to place symbol files")

View File

@ -0,0 +1,5 @@
# -*- cmake -*-
include(Prebuilt)
use_prebuilt_binary(vulkan_gltf)

View File

@ -377,7 +377,6 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
// clear buffer area to ensure we don't pick up UI elements
{
gGL.flush();
LLGLDisable no_alpha(GL_ALPHA_TEST);
gAlphaMaskProgram.setMinimumAlpha(0.0f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f( 0.f, 0.f, 0.f, 1.f );
@ -410,7 +409,6 @@ BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height, LLRenderTarget*
gGL.flush();
gGL.setSceneBlendType(LLRender::BT_REPLACE);
LLGLDisable no_alpha(GL_ALPHA_TEST);
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@ -500,7 +498,6 @@ void LLTexLayerSet::renderAlphaMaskTextures(S32 x, S32 y, S32 width, S32 height,
{
// Set the alpha channel to one (clean up after previous blending)
gGL.flush();
LLGLDisable no_alpha(GL_ALPHA_TEST);
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f( 0.f, 0.f, 0.f, 1.f );
@ -1025,7 +1022,6 @@ void LLTexLayer::calculateTexLayerColor(const param_color_list_t &param_list, LL
BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bound_target)
{
LLGLEnable color_mat(GL_COLOR_MATERIAL);
// *TODO: Is this correct?
//gPipeline.disableLights();
stop_glerror();
@ -1112,7 +1108,6 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
if( tex )
{
bool no_alpha_test = getInfo()->mWriteAllChannels;
LLGLDisable alpha_test(no_alpha_test ? GL_ALPHA_TEST : 0);
if (no_alpha_test)
{
gAlphaMaskProgram.setMinimumAlpha(0.f);
@ -1162,7 +1157,6 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height, LLRenderTarget* bou
getInfo()->mStaticImageFileName.empty() &&
color_specified )
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
gAlphaMaskProgram.setMinimumAlpha(0.000f);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@ -1260,7 +1254,6 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
if( tex )
{
LLGLSNoAlphaTest gls_no_alpha_test;
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
@ -1279,7 +1272,6 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
LLGLTexture* tex = mLocalTextureObject->getImage();
if (tex)
{
LLGLSNoAlphaTest gls_no_alpha_test;
gAlphaMaskProgram.setMinimumAlpha(0.f);
gGL.getTexUnit(0)->bind(tex);
gl_rect_2d_simple_tex( width, height );
@ -1316,7 +1308,6 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
// Note: if the first param is a mulitply, multiply against the current buffer's alpha
if( !first_param || !first_param->getMultiplyBlend() )
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
// Clear the alpha
@ -1328,7 +1319,6 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
// Accumulate alphas
LLGLSNoAlphaTest gls_no_alpha_test;
gGL.color4f( 1.f, 1.f, 1.f, 1.f );
for (LLTexLayerParamAlpha* param : mParamAlphaList)
{
@ -1350,7 +1340,6 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
LLGLTexture* tex = mLocalTextureObject->getImage();
if( tex && (tex->getComponents() == 4) )
{
LLGLSNoAlphaTest gls_no_alpha_test;
LLTexUnit::eTextureAddressMode old_mode = tex->getAddressMode();
gGL.getTexUnit(0)->bind(tex, TRUE);
@ -1370,7 +1359,6 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
{
if( (tex->getComponents() == 4) || (tex->getComponents() == 1) )
{
LLGLSNoAlphaTest gls_no_alpha_test;
gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@ -1387,7 +1375,6 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
// Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO );
if ( !is_approx_equal(layer_color.mV[VW], 1.f) )
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4fv(layer_color.mV);
gl_rect_2d_simple( width, height );
@ -1472,7 +1459,14 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
else
{ // platforms with working drivers...
glReadPixels(x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_data);
// We just want GL_ALPHA, but that isn't supported in OGL core profile 4.
static const size_t TEMP_BYTES_PER_PIXEL = 4;
U8* temp_data = (U8*)ll_aligned_malloc_32(mem_size * TEMP_BYTES_PER_PIXEL);
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, temp_data);
for (size_t pixel = 0; pixel < pixels; pixel++) {
alpha_data[pixel] = temp_data[(pixel * TEMP_BYTES_PER_PIXEL) + 3];
}
ll_aligned_free_32(temp_data);
}
}
else

View File

@ -149,7 +149,7 @@ LLTexLayerParamAlpha::LLTexLayerParamAlpha(const LLTexLayerParamAlpha& pOther)
mCachedProcessedTexture(pOther.mCachedProcessedTexture),
mStaticImageTGA(pOther.mStaticImageTGA),
mStaticImageRaw(pOther.mStaticImageRaw),
mNeedsCreateTexture(pOther.mNeedsCreateTexture),
mNeedsCreateTexture(pOther.mNeedsCreateTexture.load()),
mStaticImageInvalid(pOther.mStaticImageInvalid),
mAvgDistortionVec(pOther.mAvgDistortionVec),
mCachedEffectiveWeight(pOther.mCachedEffectiveWeight)
@ -344,7 +344,6 @@ BOOL LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP);
}
LLGLSNoAlphaTest gls_no_alpha_test;
gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
gl_rect_2d_simple_tex(width, height);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@ -361,7 +360,6 @@ BOOL LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
}
else
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gGL.color4f(0.f, 0.f, 0.f, effective_weight);
gl_rect_2d_simple(width, height);

View File

@ -100,7 +100,7 @@ private:
LLPointer<LLGLTexture> mCachedProcessedTexture;
LLPointer<LLImageTGA> mStaticImageTGA;
LLPointer<LLImageRaw> mStaticImageRaw;
BOOL mNeedsCreateTexture;
std::atomic<BOOL> mNeedsCreateTexture;
BOOL mStaticImageInvalid;
LL_ALIGN_16(LLVector4a mAvgDistortionVec);
F32 mCachedEffectiveWeight;

View File

@ -607,9 +607,7 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes()
// Kick off a decode
mDecodes[decode_id] = LLPointer<LLVorbisDecodeState>(NULL);
try
{
main_queue->postTo(
bool posted = main_queue->postTo(
general_queue,
[decode_id]() // Work done on general queue
{
@ -639,8 +637,7 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes()
enqueueFinishAudio(decode_id, decode_state);
});
}
catch (const LLThreadSafeQueueInterrupt&)
if (! posted)
{
// Shutdown
// Consider making processQueue() do a cleanup instead

View File

@ -17,6 +17,7 @@ include(Tracy)
set(llcommon_SOURCE_FILES
apply.cpp
commoncontrol.cpp
indra_constants.cpp
lazyeventapi.cpp
llallocator.cpp
@ -120,6 +121,7 @@ set(llcommon_HEADER_FILES
apply.h
chrono.h
classic_callback.h
commoncontrol.h
ctype_workaround.h
fix_macros.h
function_types.h
@ -178,6 +180,7 @@ set(llcommon_HEADER_FILES
llinitdestroyclass.h
llinitparam.h
llinstancetracker.h
llinstancetrackersubclass.h
llkeybind.h
llkeythrottle.h
llleap.h
@ -251,6 +254,7 @@ set(llcommon_HEADER_FILES
stdtypes.h
stringize.h
threadpool.h
threadpool_fwd.h
threadsafeschedule.h
timer.h
tuple.h

View File

@ -0,0 +1,106 @@
/**
* @file commoncontrol.cpp
* @author Nat Goodspeed
* @date 2022-06-08
* @brief Implementation for commoncontrol.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "commoncontrol.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llevents.h"
#include "llsdutil.h"
LLSD LL::CommonControl::access(const LLSD& params)
{
// We can't actually introduce a link-time dependency on llxml, or on any
// global LLControlGroup (*koff* gSavedSettings *koff*) but we can issue a
// runtime query. If we're running as part of a viewer with
// LLViewerControlListener, we can use that to interact with any
// instantiated LLControGroup.
LLSD response;
{
LLEventStream reply("reply");
LLTempBoundListener connection = reply.listen("listener",
[&response] (const LLSD& event)
{
response = event;
return false;
});
LLSD rparams{ params };
rparams["reply"] = reply.getName();
LLEventPumps::instance().obtain("LLViewerControl").post(rparams);
}
// LLViewerControlListener responds immediately. If it's listening at all,
// it will already have set response.
if (! response.isDefined())
{
LLTHROW(NoListener("No LLViewerControl listener instantiated"));
}
LLSD error{ response["error"] };
if (error.isDefined())
{
LLTHROW(ParamError(error));
}
response.erase("error");
response.erase("reqid");
return response;
}
/// set control group.key to defined default value
LLSD LL::CommonControl::set_default(const std::string& group, const std::string& key)
{
return access(llsd::map("op", "set",
"group", group, "key", key))["value"];
}
/// set control group.key to specified value
LLSD LL::CommonControl::set(const std::string& group, const std::string& key, const LLSD& value)
{
return access(llsd::map("op", "set",
"group", group, "key", key, "value", value))["value"];
}
/// toggle boolean control group.key
LLSD LL::CommonControl::toggle(const std::string& group, const std::string& key)
{
return access(llsd::map("op", "toggle",
"group", group, "key", key))["value"];
}
/// get the definition for control group.key, (! isDefined()) if bad
/// ["name"], ["type"], ["value"], ["comment"]
LLSD LL::CommonControl::get_def(const std::string& group, const std::string& key)
{
return access(llsd::map("op", "get",
"group", group, "key", key));
}
/// get the value of control group.key
LLSD LL::CommonControl::get(const std::string& group, const std::string& key)
{
return access(llsd::map("op", "get",
"group", group, "key", key))["value"];
}
/// get defined groups
std::vector<std::string> LL::CommonControl::get_groups()
{
auto groups{ access(llsd::map("op", "groups"))["groups"] };
return { groups.beginArray(), groups.endArray() };
}
/// get definitions for all variables in group
LLSD LL::CommonControl::get_vars(const std::string& group)
{
return access(llsd::map("op", "vars", "group", group))["vars"];
}

View File

@ -0,0 +1,75 @@
/**
* @file commoncontrol.h
* @author Nat Goodspeed
* @date 2022-06-08
* @brief Access LLViewerControl LLEventAPI, if process has one.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_COMMONCONTROL_H)
#define LL_COMMONCONTROL_H
#include <vector>
#include "llexception.h"
#include "llsd.h"
namespace LL
{
class CommonControl
{
public:
struct Error: public LLException
{
Error(const std::string& what): LLException(what) {}
};
/// Exception thrown if there's no LLViewerControl LLEventAPI
struct NoListener: public Error
{
NoListener(const std::string& what): Error(what) {}
};
struct ParamError: public Error
{
ParamError(const std::string& what): Error(what) {}
};
/// set control group.key to defined default value
static
LLSD set_default(const std::string& group, const std::string& key);
/// set control group.key to specified value
static
LLSD set(const std::string& group, const std::string& key, const LLSD& value);
/// toggle boolean control group.key
static
LLSD toggle(const std::string& group, const std::string& key);
/// get the definition for control group.key, (! isDefined()) if bad
/// ["name"], ["type"], ["value"], ["comment"]
static
LLSD get_def(const std::string& group, const std::string& key);
/// get the value of control group.key
static
LLSD get(const std::string& group, const std::string& key);
/// get defined groups
static
std::vector<std::string> get_groups();
/// get definitions for all variables in group
static
LLSD get_vars(const std::string& group);
private:
static
LLSD access(const LLSD& params);
};
} // namespace LL
#endif /* ! defined(LL_COMMONCONTROL_H) */

View File

@ -528,6 +528,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
//static
S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
LL_PROFILE_ZONE_SCOPED;
//*****************************************
LLAPRFilePoolScope scope(pool);
apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY);
@ -572,6 +573,7 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb
//static
S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
LL_PROFILE_ZONE_SCOPED;
apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
if (offset < 0)
{

View File

@ -96,6 +96,7 @@ LLAssetDictionary::LLAssetDictionary()
addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false));
addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false));
addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true));
addEntry(LLAssetType::AT_MATERIAL, new AssetEntry("MATERIAL", "material", "render material", true, true, true));
addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false));
addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE));

View File

@ -127,8 +127,9 @@ public:
AT_RESERVED_6 = 55,
AT_SETTINGS = 56, // Collection of settings
AT_MATERIAL = 57, // Render Material
AT_COUNT = 57,
AT_COUNT = 58,
// +*********************************************************+
// | TO ADD AN ELEMENT TO THIS ENUM: |

View File

@ -80,8 +80,8 @@ struct LLContextStatus
LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status);
#define dumpStack(tag) \
if (debugLoggingEnabled(tag)) \
{ \
LLCallStack cs; \
LL_DEBUGS(tag) << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL; \
}
LL_DEBUGS(tag) << "STACK:\n" \
<< "====================\n" \
<< LLCallStack() \
<< "====================" \
<< LL_ENDL;

View File

@ -37,12 +37,13 @@ thread_local bool gProfilerEnabled = false;
#if (TRACY_ENABLE)
// Override new/delete for tracy memory profiling
void *operator new(size_t size)
void* ll_tracy_new(size_t size)
{
void* ptr;
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
//LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
ptr = (malloc)(size);
}
else
@ -57,12 +58,22 @@ void *operator new(size_t size)
return ptr;
}
void operator delete(void *ptr) noexcept
void* operator new(size_t size)
{
return ll_tracy_new(size);
}
void* operator new[](std::size_t count)
{
return ll_tracy_new(count);
}
void ll_tracy_delete(void* ptr)
{
TracyFree(ptr);
if (gProfilerEnabled)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
//LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
(free)(ptr);
}
else
@ -71,6 +82,16 @@ void operator delete(void *ptr) noexcept
}
}
void operator delete(void *ptr) noexcept
{
ll_tracy_delete(ptr);
}
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.

View File

@ -1603,20 +1603,6 @@ namespace LLError
}
}
bool debugLoggingEnabled(const std::string& tag)
{
LLMutexTrylock lock(getMutex<LOG_MUTEX>(), 5);
if (!lock.isLocked())
{
return false;
}
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
LLError::ELevel level = LLError::LEVEL_DEBUG;
bool res = checkLevelMap(s->mTagLevelMap, tag, level);
return res;
}
void crashdriver(void (*callback)(int*))
{
// The LLERROR_CRASH macro used to have inline code of the form:

View File

@ -82,9 +82,11 @@ const int LL_ERR_NOERR = 0;
#ifdef SHOW_ASSERT
#define llassert(func) llassert_always_msg(func, #func)
#define llassert_msg(func, msg) llassert_always_msg(func, msg)
#define llverify(func) llassert_always_msg(func, #func)
#else
#define llassert(func)
#define llassert_msg(func, msg)
#define llverify(func) do {if (func) {}} while(0)
#endif
@ -462,8 +464,31 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
LLError::CallSite& _site(_sites[which]); \
lllog_test_()
// Check at run-time whether logging is enabled, without generating output
/*
// Check at run-time whether logging is enabled, without generating output.
Resist the temptation to add a function like this because it incurs the
expense of locking and map-searching every time control reaches it.
bool debugLoggingEnabled(const std::string& tag);
Instead of:
if debugLoggingEnabled("SomeTag")
{
// ... presumably expensive operation ...
LL_DEBUGS("SomeTag") << ... << LL_ENDL;
}
Use this:
LL_DEBUGS("SomeTag");
// ... presumably expensive operation ...
LL_CONT << ...;
LL_ENDL;
LL_DEBUGS("SomeTag") performs the locking and map-searching ONCE, then caches
the result in a static variable.
*/
// used by LLERROR_CRASH
void crashdriver(void (*)(int*));

View File

@ -29,11 +29,6 @@
#include "llframetimer.h"
// We don't bother building a stand alone lib; we just need to include the one source file for Tracy support
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#include "TracyClient.cpp"
#endif // LL_PROFILER_CONFIGURATION
// Static members
//LLTimer LLFrameTimer::sInternalTimer;
U64 LLFrameTimer::sStartTotalTime = totalTime();

View File

@ -104,22 +104,26 @@ public:
return LockStatic()->mMap.size();
}
// snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs
class snapshot
// snapshot of std::pair<const KEY, std::shared_ptr<SUBCLASS>> pairs, for
// some SUBCLASS derived from T
template <typename SUBCLASS>
class snapshot_of
{
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<std::pair<const KEY, weak_t>> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
// Dereferencing the iterator we publish produces a
// std::shared_ptr<SUBCLASS> for each instance that still exists.
// Since we store weak_ptr<T>, that involves two chained
// transformations:
// - a transform_iterator to lock the weak_ptr and return a shared_ptr
// - a filter_iterator to skip any shared_ptr that has become invalid.
// - a filter_iterator to skip any shared_ptr<T> that has become
// invalid or references any T instance that isn't SUBCLASS.
// It is very important that we filter lazily, that is, during
// traversal. Any one of our stored weak_ptrs might expire during
// traversal.
typedef std::pair<const KEY, ptr_t> strong_pair;
typedef std::pair<const KEY, std::shared_ptr<SUBCLASS>> strong_pair;
// Note for future reference: nat has not yet had any luck (up to
// Boost 1.67) trying to use boost::transform_iterator with a hand-
// coded functor, only with actual functions. In my experience, an
@ -127,7 +131,7 @@ public:
// result_type typedef. But this works.
static strong_pair strengthen(typename VectorType::value_type& pair)
{
return { pair.first, pair.second.lock() };
return { pair.first, std::dynamic_pointer_cast<SUBCLASS>(pair.second.lock()) };
}
static bool dead_skipper(const strong_pair& pair)
{
@ -135,7 +139,7 @@ public:
}
public:
snapshot():
snapshot_of():
// populate our vector with a snapshot of (locked!) InstanceMap
// note, this assigns pair<KEY, shared_ptr> to pair<KEY, weak_ptr>
mData(mLock->mMap.begin(), mLock->mMap.end())
@ -184,44 +188,51 @@ public:
#endif // LL_WINDOWS
VectorType mData;
};
using snapshot = snapshot_of<T>;
// iterate over this for references to each instance
class instance_snapshot: public snapshot
// iterate over this for references to each SUBCLASS instance
template <typename SUBCLASS>
class instance_snapshot_of: public snapshot_of<SUBCLASS>
{
private:
static T& instance_getter(typename snapshot::iterator::reference pair)
using super = snapshot_of<SUBCLASS>;
static T& instance_getter(typename super::iterator::reference pair)
{
return *pair.second;
}
public:
typedef boost::transform_iterator<decltype(instance_getter)*,
typename snapshot::iterator> iterator;
iterator begin() { return iterator(snapshot::begin(), instance_getter); }
iterator end() { return iterator(snapshot::end(), instance_getter); }
typename super::iterator> iterator;
iterator begin() { return iterator(super::begin(), instance_getter); }
iterator end() { return iterator(super::end(), instance_getter); }
void deleteAll()
{
for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it)
for (auto it(super::begin()), end(super::end()); it != end; ++it)
{
delete it->second.get();
}
}
};
using instance_snapshot = instance_snapshot_of<T>;
// iterate over this for each key
class key_snapshot: public snapshot
template <typename SUBCLASS>
class key_snapshot_of: public snapshot_of<SUBCLASS>
{
private:
static KEY key_getter(typename snapshot::iterator::reference pair)
using super = snapshot_of<SUBCLASS>;
static KEY key_getter(typename super::iterator::reference pair)
{
return pair.first;
}
public:
typedef boost::transform_iterator<decltype(key_getter)*,
typename snapshot::iterator> iterator;
iterator begin() { return iterator(snapshot::begin(), key_getter); }
iterator end() { return iterator(snapshot::end(), key_getter); }
typename super::iterator> iterator;
iterator begin() { return iterator(super::begin(), key_getter); }
iterator end() { return iterator(super::end(), key_getter); }
};
using key_snapshot = key_snapshot_of<T>;
static ptr_t getInstance(const KEY& k)
{
@ -368,22 +379,25 @@ public:
return LockStatic()->mSet.size();
}
// snapshot of std::shared_ptr<T> pointers
class snapshot
// snapshot of std::shared_ptr<SUBCLASS> pointers
template <typename SUBCLASS>
class snapshot_of
{
// It's very important that what we store in this snapshot are
// weak_ptrs, NOT shared_ptrs. That's how we discover whether any
// instance has been deleted during the lifespan of a snapshot.
typedef std::vector<weak_t> VectorType;
// Dereferencing our iterator produces a std::shared_ptr for each
// instance that still exists. Since we store weak_ptrs, that involves
// two chained transformations:
// Dereferencing the iterator we publish produces a
// std::shared_ptr<SUBCLASS> for each instance that still exists.
// Since we store weak_ptrs, that involves two chained
// transformations:
// - a transform_iterator to lock the weak_ptr and return a shared_ptr
// - a filter_iterator to skip any shared_ptr that has become invalid.
typedef std::shared_ptr<T> strong_ptr;
// - a filter_iterator to skip any shared_ptr that has become invalid
// or references any T instance that isn't SUBCLASS.
typedef std::shared_ptr<SUBCLASS> strong_ptr;
static strong_ptr strengthen(typename VectorType::value_type& ptr)
{
return ptr.lock();
return std::dynamic_pointer_cast<SUBCLASS>(ptr.lock());
}
static bool dead_skipper(const strong_ptr& ptr)
{
@ -391,7 +405,7 @@ public:
}
public:
snapshot():
snapshot_of():
// populate our vector with a snapshot of (locked!) InstanceSet
// note, this assigns stored shared_ptrs to weak_ptrs for snapshot
mData(mLock->mSet.begin(), mLock->mSet.end())
@ -437,22 +451,33 @@ public:
#endif // LL_WINDOWS
VectorType mData;
};
using snapshot = snapshot_of<T>;
// iterate over this for references to each instance
struct instance_snapshot: public snapshot
template <typename SUBCLASS>
class instance_snapshot_of: public snapshot_of<SUBCLASS>
{
typedef boost::indirect_iterator<typename snapshot::iterator> iterator;
iterator begin() { return iterator(snapshot::begin()); }
iterator end() { return iterator(snapshot::end()); }
private:
using super = snapshot_of<SUBCLASS>;
public:
typedef boost::indirect_iterator<typename super::iterator> iterator;
iterator begin() { return iterator(super::begin()); }
iterator end() { return iterator(super::end()); }
void deleteAll()
{
for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it)
for (auto it(super::begin()), end(super::end()); it != end; ++it)
{
delete it->get();
}
}
};
using instance_snapshot = instance_snapshot_of<T>;
// key_snapshot_of isn't really meaningful, but define it anyway to avoid
// requiring two different LLInstanceTrackerSubclass implementations.
template <typename SUBCLASS>
using key_snapshot_of = instance_snapshot_of<SUBCLASS>;
protected:
LLInstanceTracker()

View File

@ -0,0 +1,98 @@
/**
* @file llinstancetrackersubclass.h
* @author Nat Goodspeed
* @date 2022-12-09
* @brief Intermediate class to get subclass-specific types from
* LLInstanceTracker instance-retrieval methods.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLINSTANCETRACKERSUBCLASS_H)
#define LL_LLINSTANCETRACKERSUBCLASS_H
#include <memory> // std::shared_ptr, std::weak_ptr
/**
* Derive your subclass S of a subclass T of LLInstanceTracker<T> from
* LLInstanceTrackerSubclass<S, T> to perform appropriate downcasting and
* filtering for LLInstanceTracker access methods.
*
* LLInstanceTracker<T> uses CRTP, so that getWeak(), getInstance(), snapshot
* and instance_snapshot return pointers and references to T. The trouble is
* that subclasses T0 and T1 derived from T also get pointers and references
* to their base class T, requiring explicit downcasting. Moreover,
* T0::getInstance() shouldn't find an instance of any T subclass other than
* T0. Nor should T0::snapshot.
*
* @code
* class Tracked: public LLInstanceTracker<Tracked, std::string>
* {
* private:
* using super = LLInstanceTracker<Tracked, std::string>;
* public:
* Tracked(const std::string& name): super(name) {}
* // All references to Tracked::ptr_t, Tracked::getInstance() etc.
* // appropriately use Tracked.
* // ...
* };
*
* // But now we derive SubTracked from Tracked. We need SubTracked::ptr_t,
* // SubTracked::getInstance() etc. to use SubTracked, not Tracked.
* // This LLInstanceTrackerSubclass specialization is itself derived from
* // Tracked.
* class SubTracked: public LLInstanceTrackerSubclass<SubTracked, Tracked>
* {
* private:
* using super = LLInstanceTrackerSubclass<SubTracked, Tracked>;
* public:
* // LLInstanceTrackerSubclass's constructor forwards to Tracked's.
* SubTracked(const std::string& name): super(name) {}
* // SubTracked::getInstance() returns std::shared_ptr<SubTracked>, etc.
* // ...
* @endcode
*/
template <typename SUBCLASS, typename T>
class LLInstanceTrackerSubclass: public T
{
public:
using ptr_t = std::shared_ptr<SUBCLASS>;
using weak_t = std::weak_ptr<SUBCLASS>;
// forward any constructor call to the corresponding T ctor
template <typename... ARGS>
LLInstanceTrackerSubclass(ARGS&&... args):
T(std::forward<ARGS>(args)...)
{}
weak_t getWeak()
{
// call base-class getWeak(), try to lock, downcast to SUBCLASS
return std::dynamic_pointer_cast<SUBCLASS>(T::getWeak().lock());
}
template <typename KEY>
static ptr_t getInstance(const KEY& k)
{
return std::dynamic_pointer_cast<SUBCLASS>(T::getInstance(k));
}
using snapshot = typename T::template snapshot_of<SUBCLASS>;
using instance_snapshot = typename T::template instance_snapshot_of<SUBCLASS>;
using key_snapshot = typename T::template key_snapshot_of<SUBCLASS>;
static size_t instanceCount()
{
// T::instanceCount() lies because our snapshot, et al., won't
// necessarily return all the T instances -- only those that are also
// SUBCLASS instances. Count those.
size_t count = 0;
for (const auto& pair : snapshot())
++count;
return count;
}
};
#endif /* ! defined(LL_LLINSTANCETRACKERSUBCLASS_H) */

View File

@ -35,6 +35,7 @@
# include <sys/types.h>
# include <mach/task.h>
# include <mach/mach_init.h>
#include <mach/mach_host.h>
#elif LL_LINUX
# include <unistd.h>
#endif
@ -109,6 +110,50 @@ void LLMemory::updateMemoryInfo()
{
sAvailPhysicalMemInKB = U32Kilobytes(0);
}
#elif defined(LL_DARWIN)
task_vm_info info;
mach_msg_type_number_t infoCount = TASK_VM_INFO_COUNT;
// MACH_TASK_BASIC_INFO reports the same resident_size, but does not tell us the reusable bytes or phys_footprint.
if (task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) == KERN_SUCCESS)
{
// Our Windows definition of PagefileUsage is documented by Microsoft as "the total amount of
// memory that the memory manager has committed for a running process", which is rss.
sAllocatedPageSizeInKB = U32Bytes(info.resident_size);
// Activity Monitor => Inspect Process => Real Memory Size appears to report resident_size
// Activity monitor => main window memory column appears to report phys_footprint, which spot checks as at least 30% less.
// I think that is because of compression, which isn't going to give us a consistent measurement. We want uncompressed totals.
//
// In between is resident_size - reusable. This is what Chrome source code uses, with source comments saying it is 'the "Real Memory" value
// reported for the app by the Memory Monitor in Instruments.' It is still about 8% bigger than phys_footprint.
//
// (On Windows, we use WorkingSetSize.)
sAllocatedMemInKB = U32Bytes(info.resident_size - info.reusable);
}
else
{
LL_WARNS() << "task_info failed" << LL_ENDL;
}
// Total installed and available physical memory are properties of the host, not just our process.
vm_statistics64_data_t vmstat;
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
mach_port_t host = mach_host_self();
vm_size_t page_size;
host_page_size(host, &page_size);
kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast<host_info_t>(&vmstat), &count);
if (result == KERN_SUCCESS) {
// This is what Chrome reports as 'the "Physical Memory Free" value reported by the Memory Monitor in Instruments.'
// Note though that inactive pages are not included here and not yet free, but could become so under memory pressure.
sAvailPhysicalMemInKB = U32Bytes(vmstat.free_count * page_size);
sMaxPhysicalMemInKB = LLMemoryInfo::getHardwareMemSize();
}
else
{
LL_WARNS() << "task_info failed" << LL_ENDL;
}
#else
//not valid for other systems for now.
sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS());

View File

@ -36,7 +36,8 @@
//============================================================================
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
//#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
#define MUTEX_DEBUG 0 //disable mutex debugging as it's interfering with profiles
#if MUTEX_DEBUG
#include <map>
@ -61,7 +62,7 @@ protected:
mutable LLThread::id_t mLockingThread;
#if MUTEX_DEBUG
std::map<LLThread::id_t, BOOL> mIsLocked;
std::unordered_map<LLThread::id_t, BOOL> mIsLocked;
#endif
};

View File

@ -340,4 +340,28 @@ private:
bool mStayUnique;
};
// boost hash adapter
template <class Type>
struct boost::hash<LLPointer<Type>>
{
typedef LLPointer<Type> argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const
{
return (std::size_t) s.get();
}
};
// Adapt boost hash to std hash
namespace std
{
template<class Type> struct hash<LLPointer<Type>>
{
std::size_t operator()(LLPointer<Type> const& s) const noexcept
{
return boost::hash<LLPointer<Type>>()(s);
}
};
}
#endif

View File

@ -86,8 +86,12 @@ extern thread_local bool gProfilerEnabled;
#define TRACY_ONLY_IPV4 1
#include "Tracy.hpp"
// Mutually exclusive with detailed memory tracing
// Enable OpenGL profiling
#define LL_PROFILER_ENABLE_TRACY_OPENGL 0
// Enable RenderDoc labeling
#define LL_PROFILER_ENABLE_RENDER_DOC 0
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY
@ -104,14 +108,13 @@ extern thread_local bool gProfilerEnabled;
#define LL_PROFILE_ZONE_ERR(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0XFF0000 ) // RGB yellow
#define LL_PROFILE_ZONE_INFO(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0X00FFFF ) // RGB cyan
#define LL_PROFILE_ZONE_WARN(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0x0FFFF00 ) // RGB red
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_FAST_TIMER
#define LL_PROFILER_FRAME_END
#define LL_PROFILER_SET_THREAD_NAME( name ) (void)(name)
#define LL_RECORD_BLOCK_TIME(name) 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) // LL_PROFILE_ZONE_NAMED is a no-op when Tracy is disabled
#define LL_PROFILE_ZONE_NAMED_COLOR(name,color) // LL_PROFILE_ZONE_NAMED_COLOR is a no-op when Tracy is disabled
#define LL_PROFILE_ZONE_SCOPED // LL_PROFILE_ZONE_SCOPED is a no-op when Tracy is disabled
#define LL_PROFILE_ZONE_COLOR(name,color) // LL_RECORD_BLOCK_TIME(name)
@ -121,8 +124,6 @@ extern thread_local bool gProfilerEnabled;
#define LL_PROFILE_ZONE_ERR(name) (void)(name); // Not supported
#define LL_PROFILE_ZONE_INFO(name) (void)(name); // Not supported
#define LL_PROFILE_ZONE_WARN(name) (void)(name); // Not supported
#define LL_PROFILE_ALLOC(ptr, size) (void)(ptr); (void)(size);
#define LL_PROFILE_FREE(ptr) (void)(ptr);
#endif
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
#define LL_PROFILER_FRAME_END FrameMark
@ -138,14 +139,45 @@ extern thread_local bool gProfilerEnabled;
#define LL_PROFILE_ZONE_ERR(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0XFF0000 ) // RGB yellow
#define LL_PROFILE_ZONE_INFO(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0X00FFFF ) // RGB cyan
#define LL_PROFILE_ZONE_WARN(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0x0FFFF00 ) // RGB red
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#endif
#else
#define LL_PROFILER_FRAME_END
#define LL_PROFILER_SET_THREAD_NAME( name ) (void)(name)
#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_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);
#else
#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_LABEL_OBJECT_GL(type, name, length, label)
#if LL_PROFILER_CONFIGURATION > 1
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#else
#define LL_PROFILE_ALLOC(ptr, size) (void)(ptr); (void)(size);
#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
#define LL_LABEL_OBJECT_GL(type, name, length, label)
#endif
#include "llprofilercategories.h"
#endif // LL_PROFILER_H

View File

@ -52,7 +52,7 @@
#define LL_PROFILER_CATEGORY_ENABLE_LOGGING 1
#define LL_PROFILER_CATEGORY_ENABLE_MATERIAL 1
#define LL_PROFILER_CATEGORY_ENABLE_MEDIA 1
#define LL_PROFILER_CATEGORY_ENABLE_MEMORY 1
#define LL_PROFILER_CATEGORY_ENABLE_MEMORY 0
#define LL_PROFILER_CATEGORY_ENABLE_NETWORK 1
#define LL_PROFILER_CATEGORY_ENABLE_OCTREE 1
#define LL_PROFILER_CATEGORY_ENABLE_PIPELINE 1

View File

@ -26,20 +26,26 @@
#include "linden_common.h"
#include "llqueuedthread.h"
#include <chrono>
#include "llstl.h"
#include "lltimer.h" // ms_sleep()
#include "lltracethreadrecorder.h"
#include "llmutex.h"
//============================================================================
// MAIN THREAD
LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
LLThread(name),
mThreaded(threaded),
mIdleThread(TRUE),
mNextHandle(0),
mStarted(FALSE)
mStarted(FALSE),
mThreaded(threaded),
mRequestQueue(name, 1024 * 1024)
{
llassert(threaded); // not threaded implementation is deprecated
mMainQueue = LL::WorkQueue::getInstance("mainloop");
if (mThreaded)
{
if(should_pause)
@ -69,6 +75,11 @@ void LLQueuedThread::shutdown()
unpause(); // MAIN THREAD
if (mThreaded)
{
if (mRequestQueue.size() == 0)
{
mRequestQueue.close();
}
S32 timeout = 100;
for ( ; timeout>0; timeout--)
{
@ -104,6 +115,8 @@ void LLQueuedThread::shutdown()
{
LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
}
mRequestQueue.close();
}
//----------------------------------------------------------------------------
@ -112,6 +125,7 @@ void LLQueuedThread::shutdown()
// virtual
size_t LLQueuedThread::update(F32 max_time_ms)
{
LL_PROFILE_ZONE_SCOPED;
if (!mStarted)
{
if (!mThreaded)
@ -125,29 +139,34 @@ size_t LLQueuedThread::update(F32 max_time_ms)
size_t LLQueuedThread::updateQueue(F32 max_time_ms)
{
F64 max_time = (F64)max_time_ms * .001;
LLTimer timer;
size_t pending = 1;
LL_PROFILE_ZONE_SCOPED;
// Frame Update
if (mThreaded)
{
pending = getPending();
if(pending > 0)
// schedule a call to threadedUpdate for every call to updateQueue
if (!isQuitting())
{
mRequestQueue.post([=]()
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update");
mIdleThread = FALSE;
threadedUpdate();
mIdleThread = TRUE;
}
);
}
if(getPending() > 0)
{
unpause();
}
}
else
{
while (pending > 0)
{
pending = processNextRequest();
if (max_time && timer.getElapsedTimeF64() > max_time)
break;
mRequestQueue.runFor(std::chrono::microseconds((int) (max_time_ms*1000.f)));
threadedUpdate();
}
}
return pending;
return getPending();
}
void LLQueuedThread::incQueue()
@ -166,11 +185,7 @@ void LLQueuedThread::incQueue()
// May be called from any thread
size_t LLQueuedThread::getPending()
{
size_t res;
lockData();
res = mRequestQueue.size();
unlockData();
return res;
return mRequestQueue.size();
}
// MAIN thread
@ -195,35 +210,28 @@ void LLQueuedThread::waitOnPending()
// MAIN thread
void LLQueuedThread::printQueueStats()
{
lockData();
if (!mRequestQueue.empty())
U32 size = mRequestQueue.size();
if (size > 0)
{
QueuedRequest *req = *mRequestQueue.begin();
LL_INFOS() << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << LL_ENDL;
LL_INFOS() << llformat("Pending Requests:%d ", mRequestQueue.size()) << LL_ENDL;
}
else
{
LL_INFOS() << "Queued Thread Idle" << LL_ENDL;
}
unlockData();
}
// MAIN thread
LLQueuedThread::handle_t LLQueuedThread::generateHandle()
{
lockData();
while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
{
mNextHandle++;
}
const LLQueuedThread::handle_t res = mNextHandle++;
unlockData();
U32 res = ++mNextHandle;
return res;
}
// MAIN thread
bool LLQueuedThread::addRequest(QueuedRequest* req)
{
LL_PROFILE_ZONE_SCOPED;
if (mStatus == QUITTING)
{
return false;
@ -231,14 +239,14 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
lockData();
req->setStatus(STATUS_QUEUED);
mRequestQueue.insert(req);
mRequestHash.insert(req);
#if _DEBUG
// LL_INFOS() << llformat("LLQueuedThread::Added req [%08d]",handle) << LL_ENDL;
#endif
unlockData();
incQueue();
llassert(!mDataLock->isSelfLocked());
mRequestQueue.post([this, req]() { processRequest(req); });
return true;
}
@ -246,6 +254,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
LL_PROFILE_ZONE_SCOPED;
llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
@ -312,6 +321,7 @@ LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
@ -333,30 +343,9 @@ void LLQueuedThread::setFlags(handle_t handle, U32 flags)
unlockData();
}
void LLQueuedThread::setPriority(handle_t handle, U32 priority)
{
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
{
if(req->getStatus() == STATUS_INPROGRESS)
{
// not in list
req->setPriority(priority);
}
else if(req->getStatus() == STATUS_QUEUED)
{
// remove from list then re-insert
llverify(mRequestQueue.erase(req) == 1);
req->setPriority(priority);
mRequestQueue.insert(req);
}
}
unlockData();
}
bool LLQueuedThread::completeRequest(handle_t handle)
{
LL_PROFILE_ZONE_SCOPED;
bool res = false;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
@ -399,23 +388,19 @@ bool LLQueuedThread::check()
//============================================================================
// Runs on its OWN thread
size_t LLQueuedThread::processNextRequest()
void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req)
{
QueuedRequest *req;
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
mIdleThread = FALSE;
//threadedUpdate();
// Get next request from pool
lockData();
while(1)
{
req = NULL;
if (mRequestQueue.empty())
{
break;
}
req = *mRequestQueue.begin();
mRequestQueue.erase(mRequestQueue.begin());
if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - abort");
req->setStatus(STATUS_ABORTED);
req->finishRequest(false);
if (req->getFlags() & FLAG_AUTO_COMPLETE)
@ -424,16 +409,15 @@ size_t LLQueuedThread::processNextRequest()
req->deleteRequest();
// check();
}
continue;
unlockData();
}
else
{
llassert_always(req->getStatus() == STATUS_QUEUED);
break;
}
U32 start_priority = 0 ;
if (req)
{
req->setStatus(STATUS_INPROGRESS);
start_priority = req->getPriority();
}
unlockData();
@ -447,6 +431,7 @@ size_t LLQueuedThread::processNextRequest()
if (complete)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - complete");
lockData();
req->setStatus(STATUS_COMPLETE);
req->finishRequest(true);
@ -454,33 +439,69 @@ size_t LLQueuedThread::processNextRequest()
{
mRequestHash.erase(req);
req->deleteRequest();
// check();
// check();
}
unlockData();
}
else
{
LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - retry");
//put back on queue and try again in 0.1ms
lockData();
req->setStatus(STATUS_QUEUED);
mRequestQueue.insert(req);
unlockData();
if (mThreaded && start_priority < PRIORITY_NORMAL)
llassert(!mDataLock->isSelfLocked());
#if 0
// try again on next frame
// NOTE: tried using "post" with a time in the future, but this
// would invariably cause this thread to wait for a long time (10+ ms)
// while work is pending
bool ret = LL::WorkQueue::postMaybe(
mMainQueue,
[=]()
{
ms_sleep(1); // sleep the thread a little
LL_PROFILE_ZONE_NAMED("processRequest - retry");
mRequestQueue.post([=]()
{
LL_PROFILE_ZONE_NAMED("processRequest - retry"); // <-- not redundant, track retry on both queues
processRequest(req);
});
});
llassert(ret);
#else
using namespace std::chrono_literals;
auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms;
mRequestQueue.post([=]
{
LL_PROFILE_ZONE_NAMED("processRequest - retry");
if (LL::WorkQueue::TimePoint::clock::now() < retry_time)
{
auto sleep_time = std::chrono::duration_cast<std::chrono::milliseconds>(retry_time - LL::WorkQueue::TimePoint::clock::now());
if (sleep_time.count() > 0)
{
ms_sleep(sleep_time.count());
}
}
processRequest(req);
});
#endif
}
}
}
LLTrace::get_thread_recorder()->pushToParent();
}
return getPending();
mIdleThread = TRUE;
}
// virtual
bool LLQueuedThread::runCondition()
{
// mRunCondition must be locked here
if (mRequestQueue.empty() && mIdleThread)
if (mRequestQueue.size() == 0 && mIdleThread)
return false;
else
return true;
@ -494,18 +515,13 @@ void LLQueuedThread::run()
startThread();
mStarted = TRUE;
while (1)
/*while (1)
{
LL_PROFILE_ZONE_SCOPED;
// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
checkPause();
if (isQuitting())
{
LLTrace::get_thread_recorder()->pushToParent();
endThread();
break;
}
mIdleThread = FALSE;
threadedUpdate();
@ -514,12 +530,18 @@ void LLQueuedThread::run()
if (pending_work == 0)
{
//LL_PROFILE_ZONE_NAMED("LLQueuedThread - sleep");
mIdleThread = TRUE;
ms_sleep(1);
//ms_sleep(1);
}
//LLThread::yield(); // thread should yield after each request
}
}*/
mRequestQueue.runUntilClose();
endThread();
LL_INFOS() << "LLQueuedThread " << mName << " EXITING." << LL_ENDL;
}
// virtual
@ -539,10 +561,9 @@ void LLQueuedThread::threadedUpdate()
//============================================================================
LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 flags) :
LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
mStatus(STATUS_UNKNOWN),
mPriority(priority),
mFlags(flags)
{
}

View File

@ -36,6 +36,7 @@
#include "llthread.h"
#include "llsimplehash.h"
#include "workqueue.h"
//============================================================================
// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
@ -45,15 +46,6 @@ class LL_COMMON_API LLQueuedThread : public LLThread
{
//------------------------------------------------------------------------
public:
enum priority_t {
PRIORITY_IMMEDIATE = 0x7FFFFFFF,
PRIORITY_URGENT = 0x40000000,
PRIORITY_HIGH = 0x30000000,
PRIORITY_NORMAL = 0x20000000,
PRIORITY_LOW = 0x10000000,
PRIORITY_LOWBITS = 0x0FFFFFFF,
PRIORITY_HIGHBITS = 0x70000000
};
enum status_t {
STATUS_EXPIRED = -1,
STATUS_UNKNOWN = 0,
@ -82,27 +74,16 @@ public:
virtual ~QueuedRequest(); // use deleteRequest()
public:
QueuedRequest(handle_t handle, U32 priority, U32 flags = 0);
QueuedRequest(handle_t handle, U32 flags = 0);
status_t getStatus()
{
return mStatus;
}
U32 getPriority() const
{
return mPriority;
}
U32 getFlags() const
{
return mFlags;
}
bool higherPriority(const QueuedRequest& second) const
{
if ( mPriority == second.mPriority)
return mHashKey < second.mHashKey;
else
return mPriority > second.mPriority;
}
protected:
status_t setStatus(status_t newstatus)
@ -121,28 +102,11 @@ public:
virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
virtual void deleteRequest(); // Only method to delete a request
void setPriority(U32 pri)
{
// Only do this on a request that is not in a queued list!
mPriority = pri;
};
protected:
LLAtomicBase<status_t> mStatus;
U32 mPriority;
U32 mFlags;
};
protected:
struct queued_request_less
{
bool operator()(const QueuedRequest* lhs, const QueuedRequest* rhs) const
{
return lhs->higherPriority(*rhs); // higher priority in front of queue (set)
}
};
//------------------------------------------------------------------------
public:
@ -167,7 +131,7 @@ private:
protected:
handle_t generateHandle();
bool addRequest(QueuedRequest* req);
size_t processNextRequest(void);
void processRequest(QueuedRequest* req);
void incQueue();
public:
@ -186,7 +150,6 @@ public:
status_t getRequestStatus(handle_t handle);
void abortRequest(handle_t handle, bool autocomplete);
void setFlags(handle_t handle, U32 flags);
void setPriority(handle_t handle, U32 priority);
bool completeRequest(handle_t handle);
// This is public for support classes like LLWorkerThread,
// but generally the methods above should be used.
@ -200,8 +163,10 @@ protected:
BOOL mStarted; // required when mThreaded is false to call startThread() from update()
LLAtomicBool mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
request_queue_t mRequestQueue;
//typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
//request_queue_t mRequestQueue;
LL::WorkQueue mRequestQueue;
LL::WorkQueue::weak_t mMainQueue;
enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2
typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t;

View File

@ -475,6 +475,7 @@ LLSDNotationParser::~LLSDNotationParser()
// virtual
S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
// map: { string:object, string:object }
// array: [ object, object, object ]
// undef: !
@ -734,6 +735,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
// map: { string:object, string:object }
map = LLSD::emptyMap();
S32 parse_count = 0;
@ -794,6 +796,7 @@ S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) c
S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
// array: [ object, object, object ]
array = LLSD::emptyArray();
S32 parse_count = 0;
@ -833,6 +836,7 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_dept
bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
std::string value;
auto count = deserialize_string(istr, value, mMaxBytesLeft);
if(PARSE_FAILURE == count) return false;
@ -843,6 +847,7 @@ bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
// binary: b##"ff3120ab1"
// or: b(len)"..."
@ -945,6 +950,7 @@ LLSDBinaryParser::~LLSDBinaryParser()
// virtual
S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
/**
* Undefined: '!'<br>
* Boolean: '1' for true '0' for false<br>

View File

@ -923,6 +923,8 @@ void LLSDXMLParser::parsePart(const char *buf, llssize len)
// virtual
S32 LLSDXMLParser::doParse(std::istream& input, LLSD& data, S32 max_depth) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
#ifdef XML_PARSER_PERFORMANCE_TESTS
XML_Timer timer( &parseTime );
#endif // XML_PARSER_PERFORMANCE_TESTS

View File

@ -771,12 +771,10 @@ static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB)
}
#endif
U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
#if LL_DARWIN
// static
U32Kilobytes LLMemoryInfo::getHardwareMemSize()
{
#if LL_WINDOWS
return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
#elif LL_DARWIN
// This might work on Linux as well. Someone check...
uint64_t phys = 0;
int mib[2] = { CTL_HW, HW_MEMSIZE };
@ -785,6 +783,16 @@ U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
sysctl(mib, 2, &phys, &len, NULL, 0);
return U64Bytes(phys);
}
#endif
U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
{
#if LL_WINDOWS
return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
#elif LL_DARWIN
return getHardwareMemSize();
#elif LL_LINUX
U64 phys = 0;

View File

@ -130,6 +130,9 @@ public:
void stream(std::ostream& s) const; ///< output text info to s
U32Kilobytes getPhysicalMemoryKB() const;
#if LL_DARWIN
static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib.
#endif
//get the available memory infomation in KiloBytes.
static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb);

View File

@ -42,6 +42,7 @@
#ifdef LL_WINDOWS
const DWORD MS_VC_EXCEPTION=0x406D1388;
#pragma pack(push,8)
@ -133,6 +134,15 @@ void LLThread::threadRun()
{
#ifdef LL_WINDOWS
set_thread_name(-1, mName.c_str());
#if 0 // probably a bad idea, see usage of SetThreadIdealProcessor in LLWindowWin32)
HANDLE hThread = GetCurrentThread();
if (hThread)
{
SetThreadAffinityMask(hThread, (DWORD_PTR) 0xFFFFFFFFFFFFFFFE);
}
#endif
#endif
LL_PROFILER_SET_THREAD_NAME( mName.c_str() );

View File

@ -30,6 +30,9 @@
#include "u64.h"
#include <chrono>
#include <thread>
#if LL_WINDOWS
# include "llwin32headerslean.h"
#elif LL_LINUX || LL_DARWIN
@ -62,9 +65,18 @@ LLTimer* LLTimer::sTimer = NULL;
//---------------------------------------------------------------------------
#if LL_WINDOWS
#if 0
void ms_sleep(U32 ms)
{
Sleep(ms);
LL_PROFILE_ZONE_SCOPED;
using TimePoint = std::chrono::steady_clock::time_point;
auto resume_time = TimePoint::clock::now() + std::chrono::milliseconds(ms);
while (TimePoint::clock::now() < resume_time)
{
std::this_thread::yield(); //note: don't use LLThread::yield here to avoid yielding for too long
}
}
U32 micro_sleep(U64 us, U32 max_yields)
@ -74,6 +86,35 @@ U32 micro_sleep(U64 us, U32 max_yields)
ms_sleep((U32)(us / 1000));
return 0;
}
#else
U32 micro_sleep(U64 us, U32 max_yields)
{
LL_PROFILE_ZONE_SCOPED
#if 0
LARGE_INTEGER ft;
ft.QuadPart = -static_cast<S64>(us * 10); // '-' using relative time
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#else
Sleep(us / 1000);
#endif
return 0;
}
void ms_sleep(U32 ms)
{
LL_PROFILE_ZONE_SCOPED
micro_sleep(ms * 1000, 0);
}
#endif
#elif LL_LINUX || LL_DARWIN
static void _sleep_loop(struct timespec& thiswait)
{

View File

@ -25,7 +25,7 @@
#include "linden_common.h"
// We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes.
// We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes.
#if LL_WINDOWS
#include "llwin32headers.h"
// ugh, this is ugly. We need to straighten out our linking for this library
@ -51,7 +51,7 @@ const LLUUID LLUUID::null;
const LLTransactionID LLTransactionID::tnull;
// static
LLMutex * LLUUID::mMutex = NULL;
LLMutex* LLUUID::mMutex = NULL;
@ -148,7 +148,7 @@ U32 janky_fast_random_byes_range(U32 val)
*/
U32 janky_fast_random_seeded_bytes(U32 seed, U32 val)
{
seed = U64L(1664525) * (U64)(seed) + U64L(1013904223);
seed = U64L(1664525) * (U64)(seed)+U64L(1013904223);
return (U32)(seed) % val;
}
#endif
@ -177,23 +177,23 @@ void LLUUID::toString(std::string& out) const
}
// *TODO: deprecate
void LLUUID::toString(char *out) const
void LLUUID::toString(char* out) const
{
std::string buffer;
toString(buffer);
strcpy(out,buffer.c_str()); /* Flawfinder: ignore */
strcpy(out, buffer.c_str()); /* Flawfinder: ignore */
}
void LLUUID::toCompressedString(std::string& out) const
{
char bytes[UUID_BYTES+1];
char bytes[UUID_BYTES + 1];
memcpy(bytes, mData, UUID_BYTES); /* Flawfinder: ignore */
bytes[UUID_BYTES] = '\0';
out.assign(bytes, UUID_BYTES);
}
// *TODO: deprecate
void LLUUID::toCompressedString(char *out) const
void LLUUID::toCompressedString(char* out) const
{
memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */
out[UUID_BYTES] = '\0';
@ -213,7 +213,7 @@ std::string LLUUID::asString() const
BOOL LLUUID::set(const char* in_string, BOOL emit)
{
return set(ll_safe_string(in_string),emit);
return set(ll_safe_string(in_string), emit);
}
BOOL LLUUID::set(const std::string& in_string, BOOL emit)
@ -233,7 +233,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
// Shouldn't see any of these any more
if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */
{
if(emit)
if (emit)
{
LL_WARNS() << "Warning! Using broken UUID string format" << LL_ENDL;
}
@ -242,7 +242,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
else
{
// Bad UUID string. Spam as INFO, as most cases we don't care.
if(emit)
if (emit)
{
//don't spam the logs because a resident can't spell.
LL_WARNS() << "Bad UUID string: " << in_string << LL_ENDL;
@ -259,7 +259,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
if ((i == 4) || (i == 6) || (i == 8) || (i == 10))
{
cur_pos++;
if (broken_format && (i==10))
if (broken_format && (i == 10))
{
// Missing - in the broken format
cur_pos--;
@ -272,17 +272,17 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
mData[i] += (U8)(in_string[cur_pos] - '0');
}
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f'))
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
{
mData[i] += (U8)(10 + in_string[cur_pos] - 'a');
}
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F'))
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
{
mData[i] += (U8)(10 + in_string[cur_pos] - 'A');
}
else
{
if(emit)
if (emit)
{
LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
}
@ -297,17 +297,17 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
mData[i] += (U8)(in_string[cur_pos] - '0');
}
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f'))
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
{
mData[i] += (U8)(10 + in_string[cur_pos] - 'a');
}
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F'))
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
{
mData[i] += (U8)(10 + in_string[cur_pos] - 'A');
}
else
{
if(emit)
if (emit)
{
LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
}
@ -342,7 +342,7 @@ BOOL LLUUID::validate(const std::string& in_string)
if ((i == 4) || (i == 6) || (i == 8) || (i == 10))
{
cur_pos++;
if (broken_format && (i==10))
if (broken_format && (i == 10))
{
// Missing - in the broken format
cur_pos--;
@ -352,10 +352,10 @@ BOOL LLUUID::validate(const std::string& in_string)
if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
{
}
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f'))
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
{
}
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F'))
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
{
}
else
@ -368,10 +368,10 @@ BOOL LLUUID::validate(const std::string& in_string)
if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
{
}
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f'))
else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
{
}
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F'))
else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
{
}
else
@ -387,7 +387,7 @@ const LLUUID& LLUUID::operator^=(const LLUUID& rhs)
{
U32* me = (U32*)&(mData[0]);
const U32* other = (U32*)&(rhs.mData[0]);
for(S32 i = 0; i < 4; ++i)
for (S32 i = 0; i < 4; ++i)
{
me[i] = me[i] ^ other[i];
}
@ -413,14 +413,14 @@ void LLUUID::combine(const LLUUID& other, LLUUID& result) const
md5_uuid.raw_digest(result.mData);
}
LLUUID LLUUID::combine(const LLUUID &other) const
LLUUID LLUUID::combine(const LLUUID& other) const
{
LLUUID combination;
combine(other, combination);
return combination;
}
std::ostream& operator<<(std::ostream& s, const LLUUID &uuid)
std::ostream& operator<<(std::ostream& s, const LLUUID& uuid)
{
std::string uuid_str;
uuid.toString(uuid_str);
@ -428,11 +428,11 @@ std::ostream& operator<<(std::ostream& s, const LLUUID &uuid)
return s;
}
std::istream& operator>>(std::istream &s, LLUUID &uuid)
std::istream& operator>>(std::istream& s, LLUUID& uuid)
{
U32 i;
char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */
for (i = 0; i < UUID_STR_LENGTH-1; i++)
for (i = 0; i < UUID_STR_LENGTH - 1; i++)
{
s >> uuid_str[i];
}
@ -441,20 +441,20 @@ std::istream& operator>>(std::istream &s, LLUUID &uuid)
return s;
}
static void get_random_bytes(void *buf, int nbytes)
static void get_random_bytes(void* buf, int nbytes)
{
int i;
char *cp = (char *) buf;
char* cp = (char*)buf;
// *NOTE: If we are not using the janky generator ll_rand()
// generates at least 3 good bytes of data since it is 0 to
// RAND_MAX. This could be made more efficient by copying all the
// bytes.
for (i=0; i < nbytes; i++)
for (i = 0; i < nbytes; i++)
#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
*cp++ = janky_fast_random_bytes() & 0xFF;
* cp++ = janky_fast_random_bytes() & 0xFF;
#else
*cp++ = ll_rand() & 0xFF;
* cp++ = ll_rand() & 0xFF;
#endif
return;
}
@ -464,11 +464,11 @@ static void get_random_bytes(void *buf, int nbytes)
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
NAME_BUFFER NameBuff[30];
}ASTAT, * PASTAT;
// static
S32 LLUUID::getNodeID(unsigned char *node_id)
S32 LLUUID::getNodeID(unsigned char* node_id)
{
ASTAT Adapter;
NCB Ncb;
@ -477,32 +477,32 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
int i;
int retval = 0;
memset( &Ncb, 0, sizeof(Ncb) );
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBENUM;
Ncb.ncb_buffer = (UCHAR *)&lenum;
Ncb.ncb_buffer = (UCHAR*)&lenum;
Ncb.ncb_length = sizeof(lenum);
uRetCode = Netbios( &Ncb );
uRetCode = Netbios(&Ncb);
for(i=0; i < lenum.length ;i++)
for (i = 0; i < lenum.length; i++)
{
memset( &Ncb, 0, sizeof(Ncb) );
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBRESET;
Ncb.ncb_lana_num = lenum.lana[i];
uRetCode = Netbios( &Ncb );
uRetCode = Netbios(&Ncb);
memset( &Ncb, 0, sizeof (Ncb) );
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBASTAT;
Ncb.ncb_lana_num = lenum.lana[i];
strcpy( (char *)Ncb.ncb_callname, "* " ); /* Flawfinder: ignore */
Ncb.ncb_buffer = (unsigned char *)&Adapter;
strcpy((char*)Ncb.ncb_callname, "* "); /* Flawfinder: ignore */
Ncb.ncb_buffer = (unsigned char*)&Adapter;
Ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios( &Ncb );
if ( uRetCode == 0 )
uRetCode = Netbios(&Ncb);
if (uRetCode == 0)
{
memcpy(node_id,Adapter.adapt.adapter_address,6); /* Flawfinder: ignore */
memcpy(node_id, Adapter.adapt.adapter_address, 6); /* Flawfinder: ignore */
retval = 1;
}
}
@ -525,16 +525,16 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
#include <net/route.h>
#include <ifaddrs.h>
// static
S32 LLUUID::getNodeID(unsigned char *node_id)
// static
S32 LLUUID::getNodeID(unsigned char* node_id)
{
int i;
unsigned char *a = NULL;
struct ifaddrs *ifap, *ifa;
unsigned char* a = NULL;
struct ifaddrs* ifap, * ifa;
int rv;
S32 result = 0;
if ((rv=getifaddrs(&ifap))==-1)
if ((rv = getifaddrs(&ifap)) == -1)
{
return -1;
}
@ -545,25 +545,25 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
{
// printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family);
for(i=0; i< ifa->ifa_addr->sa_len; i++)
// printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family);
for (i = 0; i < ifa->ifa_addr->sa_len; i++)
{
// printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]);
// printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]);
}
// printf("\n");
// printf("\n");
if(ifa->ifa_addr->sa_family == AF_LINK)
if (ifa->ifa_addr->sa_family == AF_LINK)
{
// This is a link-level address
struct sockaddr_dl *lla = (struct sockaddr_dl *)ifa->ifa_addr;
struct sockaddr_dl* lla = (struct sockaddr_dl*)ifa->ifa_addr;
// printf("\tLink level address, type %02X\n", lla->sdl_type);
// printf("\tLink level address, type %02X\n", lla->sdl_type);
if(lla->sdl_type == IFT_ETHER)
if (lla->sdl_type == IFT_ETHER)
{
// Use the first ethernet MAC in the list.
// For some reason, the macro LLADDR() defined in net/if_dl.h doesn't expand correctly. This is what it would do.
a = (unsigned char *)&((lla)->sdl_data);
a = (unsigned char*)&((lla)->sdl_data);
a += (lla)->sdl_nlen;
if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
@ -611,17 +611,17 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
#endif
#endif
// static
S32 LLUUID::getNodeID(unsigned char *node_id)
// static
S32 LLUUID::getNodeID(unsigned char* node_id)
{
int sd;
struct ifreq ifr, *ifrp;
struct ifreq ifr, * ifrp;
struct ifconf ifc;
char buf[1024];
int n, i;
unsigned char *a;
unsigned char* a;
/*
/*
* BSD 4.4 defines the size of an ifreq to be
* max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
* However, under earlier systems, sa_len isn't present, so the size is
@ -644,23 +644,23 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
memset(buf, 0, sizeof(buf));
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
if (ioctl(sd, SIOCGIFCONF, (char*)&ifc) < 0) {
close(sd);
return -1;
}
n = ifc.ifc_len;
for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
for (i = 0; i < n; i += ifreq_size(*ifr)) {
ifrp = (struct ifreq*)((char*)ifc.ifc_buf + i);
strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); /* Flawfinder: ignore */
#ifdef SIOCGIFHWADDR
if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
continue;
a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
a = (unsigned char*)&ifr.ifr_hwaddr.sa_data;
#else
#ifdef SIOCGENADDR
if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
continue;
a = (unsigned char *) ifr.ifr_enaddr;
a = (unsigned char*)ifr.ifr_enaddr;
#else
/*
* XXX we don't have a way of getting the hardware
@ -684,7 +684,7 @@ S32 LLUUID::getNodeID(unsigned char *node_id)
#endif
S32 LLUUID::cmpTime(uuid_time_t *t1, uuid_time_t *t2)
S32 LLUUID::cmpTime(uuid_time_t* t1, uuid_time_t* t2)
{
// Compare two time values.
@ -695,20 +695,20 @@ S32 LLUUID::cmpTime(uuid_time_t *t1, uuid_time_t *t2)
return 0;
}
void LLUUID::getSystemTime(uuid_time_t *timestamp)
void LLUUID::getSystemTime(uuid_time_t* timestamp)
{
// Get system time with 100ns precision. Time is since Oct 15, 1582.
#if LL_WINDOWS
ULARGE_INTEGER time;
GetSystemTimeAsFileTime((FILETIME *)&time);
GetSystemTimeAsFileTime((FILETIME*)&time);
// NT keeps time in FILETIME format which is 100ns ticks since
// Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
// The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec)
// + 18 years and 5 leap days.
time.QuadPart +=
(unsigned __int64) (1000*1000*10) // seconds
* (unsigned __int64) (60 * 60 * 24) // days
* (unsigned __int64) (17+30+31+365*18+5); // # of days
(unsigned __int64)(1000 * 1000 * 10) // seconds
* (unsigned __int64)(60 * 60 * 24) // days
* (unsigned __int64)(17 + 30 + 31 + 365 * 18 + 5); // # of days
timestamp->high = time.HighPart;
timestamp->low = time.LowPart;
@ -721,12 +721,12 @@ void LLUUID::getSystemTime(uuid_time_t *timestamp)
// Unix base time is January 1, 1970.
U64 uuid_time = ((U64)tp.tv_sec * 10000000) + (tp.tv_usec * 10) +
U64L(0x01B21DD213814000);
timestamp->high = (U32) (uuid_time >> 32);
timestamp->low = (U32) (uuid_time & 0xFFFFFFFF);
timestamp->high = (U32)(uuid_time >> 32);
timestamp->low = (U32)(uuid_time & 0xFFFFFFFF);
#endif
}
void LLUUID::getCurrentTime(uuid_time_t *timestamp)
void LLUUID::getCurrentTime(uuid_time_t* timestamp)
{
// Get current time as 60 bit 100ns ticks since whenever.
// Compensate for the fact that real clock resolution is less
@ -745,7 +745,7 @@ void LLUUID::getCurrentTime(uuid_time_t *timestamp)
mMutex = new LLMutex();
}
uuid_time_t time_now = {0,0};
uuid_time_t time_now = { 0,0 };
while (1) {
getSystemTime(&time_now);
@ -770,7 +770,8 @@ void LLUUID::getCurrentTime(uuid_time_t *timestamp)
time_now.low += uuids_this_tick;
if (!(time_now.low & 0x80000000))
time_now.high++;
} else
}
else
time_now.low += uuids_this_tick;
}
@ -787,7 +788,7 @@ void LLUUID::generate()
static int has_init = 0;
// Create a UUID.
static uuid_time_t time_last = {0,0};
static uuid_time_t time_last = { 0,0 };
static U16 clock_seq = 0;
#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
static U32 seed = 0L; // dummy seed. reset it below
@ -834,32 +835,32 @@ void LLUUID::generate()
time_last = timestamp;
memcpy(mData+10, node_id, 6); /* Flawfinder: ignore */
memcpy(mData + 10, node_id, 6); /* Flawfinder: ignore */
U32 tmp;
tmp = timestamp.low;
mData[3] = (unsigned char) tmp;
mData[3] = (unsigned char)tmp;
tmp >>= 8;
mData[2] = (unsigned char) tmp;
mData[2] = (unsigned char)tmp;
tmp >>= 8;
mData[1] = (unsigned char) tmp;
mData[1] = (unsigned char)tmp;
tmp >>= 8;
mData[0] = (unsigned char) tmp;
mData[0] = (unsigned char)tmp;
tmp = (U16) timestamp.high;
mData[5] = (unsigned char) tmp;
tmp = (U16)timestamp.high;
mData[5] = (unsigned char)tmp;
tmp >>= 8;
mData[4] = (unsigned char) tmp;
mData[4] = (unsigned char)tmp;
tmp = (timestamp.high >> 16) | 0x1000;
mData[7] = (unsigned char) tmp;
mData[7] = (unsigned char)tmp;
tmp >>= 8;
mData[6] = (unsigned char) tmp;
mData[6] = (unsigned char)tmp;
tmp = our_clock_seq;
mData[9] = (unsigned char) tmp;
mData[9] = (unsigned char)tmp;
tmp >>= 8;
mData[8] = (unsigned char) tmp;
mData[8] = (unsigned char)tmp;
HBXXH128::digest(*this, (const void*)mData, 16);
}
@ -880,9 +881,9 @@ U32 LLUUID::getRandomSeed()
// time from generating the same seed.
pid_t pid = LLApp::getPid();
seed[6]=(unsigned char)(pid >> 8);
seed[7]=(unsigned char)(pid);
getSystemTime((uuid_time_t *)(&seed[8]));
seed[6] = (unsigned char)(pid >> 8);
seed[7] = (unsigned char)(pid);
getSystemTime((uuid_time_t*)(&seed[8]));
U64 seed64 = HBXXH64::digest((const void*)seed, 16);
return U32(seed64) ^ U32(seed64 >> 32);
@ -890,16 +891,16 @@ U32 LLUUID::getRandomSeed()
BOOL LLUUID::parseUUID(const std::string& buf, LLUUID* value)
{
if( buf.empty() || value == NULL)
if (buf.empty() || value == NULL)
{
return FALSE;
}
std::string temp( buf );
std::string temp(buf);
LLStringUtil::trim(temp);
if( LLUUID::validate( temp ) )
if (LLUUID::validate(temp))
{
value->set( temp );
value->set(temp);
return TRUE;
}
return FALSE;
@ -942,9 +943,9 @@ LLUUID::LLUUID()
// Faster than copying from memory
void LLUUID::setNull()
void LLUUID::setNull()
{
U32 *word = (U32 *)mData;
U32* word = (U32*)mData;
word[0] = 0;
word[1] = 0;
word[2] = 0;
@ -953,10 +954,10 @@ LLUUID::LLUUID()
// Compare
bool LLUUID::operator==(const LLUUID& rhs) const
bool LLUUID::operator==(const LLUUID& rhs) const
{
U32 *tmp = (U32 *)mData;
U32 *rhstmp = (U32 *)rhs.mData;
U32* tmp = (U32*)mData;
U32* rhstmp = (U32*)rhs.mData;
// Note: binary & to avoid branching
return
(tmp[0] == rhstmp[0]) &
@ -966,10 +967,10 @@ LLUUID::LLUUID()
}
bool LLUUID::operator!=(const LLUUID& rhs) const
bool LLUUID::operator!=(const LLUUID& rhs) const
{
U32 *tmp = (U32 *)mData;
U32 *rhstmp = (U32 *)rhs.mData;
U32* tmp = (U32*)mData;
U32* rhstmp = (U32*)rhs.mData;
// Note: binary | to avoid branching
return
(tmp[0] != rhstmp[0]) |
@ -988,22 +989,22 @@ LLUUID::LLUUID()
}
*/
BOOL LLUUID::notNull() const
BOOL LLUUID::notNull() const
{
U32 *word = (U32 *)mData;
U32* word = (U32*)mData;
return (word[0] | word[1] | word[2] | word[3]) > 0;
}
// Faster than == LLUUID::null because doesn't require
// as much memory access.
BOOL LLUUID::isNull() const
BOOL LLUUID::isNull() const
{
U32 *word = (U32 *)mData;
U32* word = (U32*)mData;
// If all bits are zero, return !0 == TRUE
return !(word[0] | word[1] | word[2] | word[3]);
}
LLUUID::LLUUID(const char *in_string)
LLUUID::LLUUID(const char* in_string)
{
if (!in_string || in_string[0] == 0)
{
@ -1014,7 +1015,7 @@ LLUUID::LLUUID()
set(in_string);
}
LLUUID::LLUUID(const std::string& in_string)
LLUUID::LLUUID(const std::string& in_string)
{
if (in_string.empty())
{
@ -1027,12 +1028,12 @@ LLUUID::LLUUID()
// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order
// IW: this will make me very sad
bool LLUUID::operator<(const LLUUID &rhs) const
bool LLUUID::operator<(const LLUUID& rhs) const
{
U32 i;
for( i = 0; i < (UUID_BYTES - 1); i++ )
for (i = 0; i < (UUID_BYTES - 1); i++)
{
if( mData[i] != rhs.mData[i] )
if (mData[i] != rhs.mData[i])
{
return (mData[i] < rhs.mData[i]);
}
@ -1040,12 +1041,12 @@ LLUUID::LLUUID()
return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]);
}
bool LLUUID::operator>(const LLUUID &rhs) const
bool LLUUID::operator>(const LLUUID& rhs) const
{
U32 i;
for( i = 0; i < (UUID_BYTES - 1); i++ )
for (i = 0; i < (UUID_BYTES - 1); i++)
{
if( mData[i] != rhs.mData[i] )
if (mData[i] != rhs.mData[i])
{
return (mData[i] > rhs.mData[i]);
}
@ -1053,10 +1054,10 @@ LLUUID::LLUUID()
return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]);
}
U16 LLUUID::getCRC16() const
U16 LLUUID::getCRC16() const
{
// A UUID is 16 bytes, or 8 shorts.
U16 *short_data = (U16*)mData;
U16* short_data = (U16*)mData;
U16 out = 0;
out += short_data[0];
out += short_data[1];
@ -1069,8 +1070,8 @@ LLUUID::LLUUID()
return out;
}
U32 LLUUID::getCRC32() const
U32 LLUUID::getCRC32() const
{
U32 *tmp = (U32*)mData;
U32* tmp = (U32*)mData;
return tmp[0] + tmp[1] + tmp[2] + tmp[3];
}

View File

@ -73,6 +73,7 @@ void LLWorkerThread::clearDeleteList()
{
worker->mRequestHandle = LLWorkerThread::nullHandle();
worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
worker->clearFlags(LLWorkerClass::WCF_WORKING);
delete worker;
}
mDeleteList.clear() ;
@ -97,6 +98,7 @@ size_t LLWorkerThread::update(F32 max_time_ms)
{
if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED))
{
worker->setFlags(LLWorkerClass::WCF_DELETE_REQUESTED);
delete_list.push_back(worker);
mDeleteList.erase(curiter);
}
@ -130,11 +132,11 @@ size_t LLWorkerThread::update(F32 max_time_ms)
//----------------------------------------------------------------------------
LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority)
LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param)
{
handle_t handle = generateHandle();
WorkRequest* req = new WorkRequest(handle, priority, workerclass, param);
WorkRequest* req = new WorkRequest(handle, workerclass, param);
bool res = addRequest(req);
if (!res)
@ -157,8 +159,8 @@ void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass)
//============================================================================
// Runs on its OWN thread
LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
LLQueuedThread::QueuedRequest(handle, priority),
LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param) :
LLQueuedThread::QueuedRequest(handle),
mWorkerClass(workerclass),
mParam(param)
{
@ -177,6 +179,7 @@ void LLWorkerThread::WorkRequest::deleteRequest()
// virtual
bool LLWorkerThread::WorkRequest::processRequest()
{
LL_PROFILE_ZONE_SCOPED;
LLWorkerClass* workerclass = getWorkerClass();
workerclass->setWorking(true);
bool complete = workerclass->doWork(getParam());
@ -187,6 +190,7 @@ bool LLWorkerThread::WorkRequest::processRequest()
// virtual
void LLWorkerThread::WorkRequest::finishRequest(bool completed)
{
LL_PROFILE_ZONE_SCOPED;
LLWorkerClass* workerclass = getWorkerClass();
workerclass->finishWork(getParam(), completed);
U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED);
@ -200,7 +204,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
: mWorkerThread(workerthread),
mWorkerClassName(name),
mRequestHandle(LLWorkerThread::nullHandle()),
mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
mMutex(),
mWorkFlags(0)
{
@ -289,7 +292,7 @@ bool LLWorkerClass::yield()
//----------------------------------------------------------------------------
// calls startWork, adds doWork() to queue
void LLWorkerClass::addWork(S32 param, U32 priority)
void LLWorkerClass::addWork(S32 param)
{
mMutex.lock();
llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
@ -303,7 +306,7 @@ void LLWorkerClass::addWork(S32 param, U32 priority)
startWork(param);
clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
setFlags(WCF_HAVE_WORK);
mRequestHandle = mWorkerThread->addWorkRequest(this, param, priority);
mRequestHandle = mWorkerThread->addWorkRequest(this, param);
mMutex.unlock();
}
@ -318,7 +321,6 @@ void LLWorkerClass::abortWork(bool autocomplete)
if (mRequestHandle != LLWorkerThread::nullHandle())
{
mWorkerThread->abortRequest(mRequestHandle, autocomplete);
mWorkerThread->setPriority(mRequestHandle, LLQueuedThread::PRIORITY_IMMEDIATE);
setFlags(WCF_ABORT_REQUESTED);
}
mMutex.unlock();
@ -392,16 +394,5 @@ void LLWorkerClass::scheduleDelete()
}
}
void LLWorkerClass::setPriority(U32 priority)
{
mMutex.lock();
if (mRequestHandle != LLWorkerThread::nullHandle() && mRequestPriority != priority)
{
mRequestPriority = priority;
mWorkerThread->setPriority(mRequestHandle, priority);
}
mMutex.unlock();
}
//============================================================================

View File

@ -56,7 +56,7 @@ public:
virtual ~WorkRequest(); // use deleteRequest()
public:
WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param);
S32 getParam()
{
@ -90,7 +90,7 @@ public:
/*virtual*/ size_t update(F32 max_time_ms);
handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param);
S32 getNumDeletes() { return (S32)mDeleteList.size(); } // debug
@ -151,10 +151,6 @@ public:
bool isWorking() { return getFlags(WCF_WORKING); }
bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
// setPriority(): changes the priority of a request
void setPriority(U32 priority);
U32 getPriority() { return mRequestPriority; }
const std::string& getName() const { return mWorkerClassName; }
protected:
@ -169,7 +165,7 @@ protected:
void setWorkerThread(LLWorkerThread* workerthread);
// addWork(): calls startWork, adds doWork() to queue
void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL);
void addWork(S32 param);
// abortWork(): requests that work be aborted
void abortWork(bool autocomplete);
@ -193,7 +189,6 @@ protected:
LLWorkerThread* mWorkerThread;
std::string mWorkerClassName;
handle_t mRequestHandle;
U32 mRequestPriority; // last priority set
private:
LLMutex mMutex;

View File

@ -38,7 +38,7 @@ namespace tut
{
struct workqueue_data
{
WorkQueue queue{"queue"};
WorkSchedule queue{"queue"};
};
typedef test_group<workqueue_data> workqueue_group;
typedef workqueue_group::object object;
@ -49,8 +49,8 @@ namespace tut
{
set_test_name("name");
ensure_equals("didn't capture name", queue.getKey(), "queue");
ensure("not findable", WorkQueue::getInstance("queue") == queue.getWeak().lock());
WorkQueue q2;
ensure("not findable", WorkSchedule::getInstance("queue") == queue.getWeak().lock());
WorkSchedule q2;
ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue"));
}
@ -73,16 +73,16 @@ namespace tut
{
set_test_name("postEvery");
// record of runs
using Shared = std::deque<WorkQueue::TimePoint>;
using Shared = std::deque<WorkSchedule::TimePoint>;
// This is an example of how to share data between the originator of
// postEvery(work) and the work item itself, since usually a WorkQueue
// postEvery(work) and the work item itself, since usually a WorkSchedule
// is used to dispatch work to a different thread. Neither of them
// should call any of LLCond's wait methods: you don't want to stall
// either the worker thread or the originating thread (conventionally
// main). Use LLCond or a subclass even if all you want to do is
// signal the work item that it can quit; consider LLOneShotCond.
LLCond<Shared> data;
auto start = WorkQueue::TimePoint::clock::now();
auto start = WorkSchedule::TimePoint::clock::now();
// 2s seems like a long time to wait, since it directly impacts the
// duration of this test program. Unfortunately GitHub's Mac runners
// are pretty wimpy, and we're getting spurious "too late" errors just
@ -97,7 +97,7 @@ namespace tut
data.update_one(
[](Shared& data)
{
data.push_back(WorkQueue::TimePoint::clock::now());
data.push_back(WorkSchedule::TimePoint::clock::now());
});
// by the 3rd call, return false to stop
return (++count < 3);
@ -106,7 +106,7 @@ namespace tut
// postEvery() running, so run until we have exhausted the iterations
// or we time out waiting
for (auto finish = start + 10*interval;
WorkQueue::TimePoint::clock::now() < finish &&
WorkSchedule::TimePoint::clock::now() < finish &&
data.get([](const Shared& data){ return data.size(); }) < 3; )
{
queue.runPending();
@ -143,8 +143,8 @@ namespace tut
void object::test<4>()
{
set_test_name("postTo");
WorkQueue main("main");
auto qptr = WorkQueue::getInstance("queue");
WorkSchedule main("main");
auto qptr = WorkSchedule::getInstance("queue");
int result = 0;
main.postTo(
qptr,
@ -175,8 +175,8 @@ namespace tut
void object::test<5>()
{
set_test_name("postTo with void return");
WorkQueue main("main");
auto qptr = WorkQueue::getInstance("queue");
WorkSchedule main("main");
auto qptr = WorkSchedule::getInstance("queue");
std::string observe;
main.postTo(
qptr,
@ -198,7 +198,7 @@ namespace tut
std::string stored;
// Try to call waitForResult() on this thread's main coroutine. It
// should throw because the main coroutine must service the queue.
auto what{ catch_what<WorkQueue::Error>(
auto what{ catch_what<WorkSchedule::Error>(
[this, &stored](){ stored = queue.waitForResult(
[](){ return "should throw"; }); }) };
ensure("lambda should not have run", stored.empty());

View File

@ -17,18 +17,58 @@
// std headers
// external library headers
// other Linden headers
#include "commoncontrol.h"
#include "llerror.h"
#include "llevents.h"
#include "llsd.h"
#include "stringize.h"
LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity):
#include <boost/fiber/algo/round_robin.hpp>
/*****************************************************************************
* Custom fiber scheduler for worker threads
*****************************************************************************/
// As of 2022-12-06, each of our worker threads only runs a single (default)
// fiber: we don't launch explicit fibers within worker threads, nor do we
// anticipate doing so. So a worker thread that's simply waiting for incoming
// tasks should really sleep a little. Override the default fiber scheduler to
// implement that.
struct sleepy_robin: public boost::fibers::algo::round_robin
{
virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept
{
#if LL_WINDOWS
// round_robin holds a std::condition_variable, and
// round_robin::suspend_until() calls
// std::condition_variable::wait_until(). On Windows, that call seems
// busier than it ought to be. Try just sleeping.
Sleep(1);
#else
// currently unused other than windows, but might as well have something here
// different units than Sleep(), but we actually just want to sleep for any de-minimis duration
usleep(1);
#endif
}
virtual void notify() noexcept
{
// Since our Sleep() call above will wake up on its own, we need not
// take any special action to wake it.
}
};
/*****************************************************************************
* ThreadPoolBase
*****************************************************************************/
LL::ThreadPoolBase::ThreadPoolBase(const std::string& name, size_t threads,
WorkQueueBase* queue):
super(name),
mQueue(name, capacity),
mName("ThreadPool:" + name),
mThreadCount(threads)
mThreadCount(getConfiguredWidth(name, threads)),
mQueue(queue)
{}
void LL::ThreadPool::start()
void LL::ThreadPoolBase::start()
{
for (size_t i = 0; i < mThreadCount; ++i)
{
@ -56,17 +96,17 @@ void LL::ThreadPool::start()
});
}
LL::ThreadPool::~ThreadPool()
LL::ThreadPoolBase::~ThreadPoolBase()
{
close();
}
void LL::ThreadPool::close()
void LL::ThreadPoolBase::close()
{
if (! mQueue.isClosed())
if (! mQueue->isClosed())
{
LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL;
mQueue.close();
mQueue->close();
for (auto& pair: mThreads)
{
LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL;
@ -76,14 +116,74 @@ void LL::ThreadPool::close()
}
}
void LL::ThreadPool::run(const std::string& name)
void LL::ThreadPoolBase::run(const std::string& name)
{
#if LL_WINDOWS
// Try using sleepy_robin fiber scheduler.
boost::fibers::use_scheduling_algorithm<sleepy_robin>();
#endif // LL_WINDOWS
LL_DEBUGS("ThreadPool") << name << " starting" << LL_ENDL;
run();
LL_DEBUGS("ThreadPool") << name << " stopping" << LL_ENDL;
}
void LL::ThreadPool::run()
void LL::ThreadPoolBase::run()
{
mQueue.runUntilClose();
mQueue->runUntilClose();
}
//static
size_t LL::ThreadPoolBase::getConfiguredWidth(const std::string& name, size_t dft)
{
LLSD poolSizes;
try
{
poolSizes = LL::CommonControl::get("Global", "ThreadPoolSizes");
// "ThreadPoolSizes" is actually a map containing the sizes of
// interest -- or should be, if this process has an
// LLViewerControlListener instance and its settings include
// "ThreadPoolSizes". If we failed to retrieve it, perhaps we're in a
// program that doesn't define that, or perhaps there's no such
// setting, or perhaps we're asking too early, before the LLEventAPI
// itself has been instantiated. In any of those cases, it seems worth
// warning.
if (! poolSizes.isDefined())
{
// Note: we don't warn about absence of an override key for a
// particular ThreadPool name, that's fine. This warning is about
// complete absence of a ThreadPoolSizes setting, which we expect
// in a normal viewer session.
LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '"
<< name << "'" << LL_ENDL;
}
}
catch (const LL::CommonControl::Error& exc)
{
// We don't want ThreadPool to *require* LLViewerControlListener.
// Just log it and carry on.
LL_WARNS("ThreadPool") << "Can't check 'ThreadPoolSizes': " << exc.what() << LL_ENDL;
}
LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL;
// LLSD treats an undefined value as an empty map when asked to retrieve a
// key, so we don't need this to be conditional.
LLSD sizeSpec{ poolSizes[name] };
// We retrieve sizeSpec as LLSD, rather than immediately as LLSD::Integer,
// so we can distinguish the case when it's undefined.
return sizeSpec.isInteger() ? sizeSpec.asInteger() : dft;
}
//static
size_t LL::ThreadPoolBase::getWidth(const std::string& name, size_t dft)
{
auto instance{ getInstance(name) };
if (instance)
{
return instance->getWidth();
}
else
{
return getConfiguredWidth(name, dft);
}
}

View File

@ -13,7 +13,9 @@
#if ! defined(LL_THREADPOOL_H)
#define LL_THREADPOOL_H
#include "threadpool_fwd.h"
#include "workqueue.h"
#include <memory> // std::unique_ptr
#include <string>
#include <thread>
#include <utility> // std::pair
@ -22,17 +24,24 @@
namespace LL
{
class ThreadPool: public LLInstanceTracker<ThreadPool, std::string>
class ThreadPoolBase: public LLInstanceTracker<ThreadPoolBase, std::string>
{
private:
using super = LLInstanceTracker<ThreadPool, std::string>;
using super = LLInstanceTracker<ThreadPoolBase, std::string>;
public:
/**
* Pass ThreadPool a string name. This can be used to look up the
* Pass ThreadPoolBase a string name. This can be used to look up the
* relevant WorkQueue.
*
* The number of threads you pass sets the compile-time default. But
* if the user has overridden the LLSD map in the "ThreadPoolSizes"
* setting with a key matching this ThreadPool name, that setting
* overrides this parameter.
*/
ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024);
virtual ~ThreadPool();
ThreadPoolBase(const std::string& name, size_t threads,
WorkQueueBase* queue);
virtual ~ThreadPoolBase();
/**
* Launch the ThreadPool. Until this call, a constructed ThreadPool
@ -50,8 +59,6 @@ namespace LL
std::string getName() const { return mName; }
size_t getWidth() const { return mThreads.size(); }
/// obtain a non-const reference to the WorkQueue to post work to it
WorkQueue& getQueue() { return mQueue; }
/**
* Override run() if you need special processing. The default run()
@ -59,15 +66,72 @@ namespace LL
*/
virtual void run();
/**
* getConfiguredWidth() returns the setting, if any, for the specified
* ThreadPool name. Returns dft if the "ThreadPoolSizes" map does not
* contain the specified name.
*/
static
size_t getConfiguredWidth(const std::string& name, size_t dft=0);
/**
* This getWidth() returns the width of the instantiated ThreadPool
* with the specified name, if any. If no instance exists, returns its
* getConfiguredWidth() if any. If there's no instance and no relevant
* override, return dft. Presumably dft should match the threads
* parameter passed to the ThreadPool constructor call that will
* eventually instantiate the ThreadPool with that name.
*/
static
size_t getWidth(const std::string& name, size_t dft);
protected:
std::unique_ptr<WorkQueueBase> mQueue;
private:
void run(const std::string& name);
WorkQueue mQueue;
std::string mName;
size_t mThreadCount;
std::vector<std::pair<std::string, std::thread>> mThreads;
};
/**
* Specialize with WorkQueue or, for timestamped tasks, WorkSchedule
*/
template <class QUEUE>
struct ThreadPoolUsing: public ThreadPoolBase
{
using queue_t = QUEUE;
/**
* Pass ThreadPoolUsing a string name. This can be used to look up the
* relevant WorkQueue.
*
* The number of threads you pass sets the compile-time default. But
* if the user has overridden the LLSD map in the "ThreadPoolSizes"
* setting with a key matching this ThreadPool name, that setting
* overrides this parameter.
*
* Pass an explicit capacity to limit the size of the queue.
* Constraining the queue can cause a submitter to block. Do not
* constrain any ThreadPool accepting work from the main thread.
*/
ThreadPoolUsing(const std::string& name, size_t threads=1, size_t capacity=1024*1024):
ThreadPoolBase(name, threads, new queue_t(name, capacity))
{}
~ThreadPoolUsing() override {}
/**
* obtain a non-const reference to the specific WorkQueue subclass to
* post work to it
*/
queue_t& getQueue() { return static_cast<queue_t&>(*mQueue); }
};
/// ThreadPool is shorthand for using the simpler WorkQueue
using ThreadPool = ThreadPoolUsing<WorkQueue>;
} // namespace LL
#endif /* ! defined(LL_THREADPOOL_H) */

View File

@ -0,0 +1,25 @@
/**
* @file threadpool_fwd.h
* @author Nat Goodspeed
* @date 2022-12-09
* @brief Forward declarations for ThreadPool et al.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_THREADPOOL_FWD_H)
#define LL_THREADPOOL_FWD_H
#include "workqueue.h"
namespace LL
{
template <class QUEUE>
struct ThreadPoolUsing;
using ThreadPool = ThreadPoolUsing<WorkQueue>;
} // namespace LL
#endif /* ! defined(LL_THREADPOOL_FWD_H) */

View File

@ -26,14 +26,121 @@
using Mutex = LLCoros::Mutex;
using Lock = LLCoros::LockType;
LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity):
super(makeName(name)),
mQueue(capacity)
/*****************************************************************************
* WorkQueueBase
*****************************************************************************/
LL::WorkQueueBase::WorkQueueBase(const std::string& name):
super(makeName(name))
{
// TODO: register for "LLApp" events so we can implicitly close() on
// viewer shutdown.
}
void LL::WorkQueueBase::runUntilClose()
{
try
{
for (;;)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
callWork(pop_());
}
}
catch (const Closed&)
{
}
}
bool LL::WorkQueueBase::runPending()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
for (Work work; tryPop_(work); )
{
callWork(work);
}
return ! done();
}
bool LL::WorkQueueBase::runOne()
{
Work work;
if (tryPop_(work))
{
callWork(work);
}
return ! done();
}
bool LL::WorkQueueBase::runUntil(const TimePoint& until)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// Should we subtract some slop to allow for typical Work execution time?
// How much slop?
// runUntil() is simply a time-bounded runPending().
for (Work work; TimePoint::clock::now() < until && tryPop_(work); )
{
callWork(work);
}
return ! done();
}
std::string LL::WorkQueueBase::makeName(const std::string& name)
{
if (! name.empty())
return name;
static U32 discriminator = 0;
static Mutex mutex;
U32 num;
{
// Protect discriminator from concurrent access by different threads.
// It can't be thread_local, else two racing threads will come up with
// the same name.
Lock lk(mutex);
num = discriminator++;
}
return STRINGIZE("WorkQueue" << num);
}
void LL::WorkQueueBase::callWork(const Work& work)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
try
{
work();
}
catch (...)
{
// 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());
}
}
void LL::WorkQueueBase::error(const std::string& msg)
{
LL_ERRS("WorkQueue") << msg << LL_ENDL;
}
void LL::WorkQueueBase::checkCoroutine(const std::string& method)
{
// By convention, the default coroutine on each thread has an empty name
// string. See also LLCoros::logname().
if (LLCoros::getName().empty())
{
LLTHROW(Error("Do not call " + method + " from a thread's default coroutine"));
}
}
/*****************************************************************************
* WorkQueue
*****************************************************************************/
LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity):
super(name),
mQueue(capacity)
{
}
void LL::WorkQueue::close()
{
mQueue.close();
@ -54,105 +161,85 @@ bool LL::WorkQueue::done()
return mQueue.done();
}
void LL::WorkQueue::runUntilClose()
bool LL::WorkQueue::post(const Work& callable)
{
try
{
for (;;)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
callWork(mQueue.pop());
}
}
catch (const Queue::Closed&)
{
}
return mQueue.pushIfOpen(callable);
}
bool LL::WorkQueue::runPending()
bool LL::WorkQueue::tryPost(const Work& callable)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
for (Work work; mQueue.tryPop(work); )
{
callWork(work);
}
return ! mQueue.done();
return mQueue.tryPush(callable);
}
bool LL::WorkQueue::runOne()
LL::WorkQueue::Work LL::WorkQueue::pop_()
{
Work work;
if (mQueue.tryPop(work))
{
callWork(work);
}
return ! mQueue.done();
return mQueue.pop();
}
bool LL::WorkQueue::runUntil(const TimePoint& until)
bool LL::WorkQueue::tryPop_(Work& work)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
// Should we subtract some slop to allow for typical Work execution time?
// How much slop?
// runUntil() is simply a time-bounded runPending().
for (Work work; TimePoint::clock::now() < until && mQueue.tryPop(work); )
{
callWork(work);
}
return ! mQueue.done();
return mQueue.tryPop(work);
}
std::string LL::WorkQueue::makeName(const std::string& name)
/*****************************************************************************
* WorkSchedule
*****************************************************************************/
LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity):
super(name),
mQueue(capacity)
{
if (! name.empty())
return name;
static U32 discriminator = 0;
static Mutex mutex;
U32 num;
{
// Protect discriminator from concurrent access by different threads.
// It can't be thread_local, else two racing threads will come up with
// the same name.
Lock lk(mutex);
num = discriminator++;
}
return STRINGIZE("WorkQueue" << num);
}
void LL::WorkQueue::callWork(const Queue::DataTuple& work)
void LL::WorkSchedule::close()
{
// ThreadSafeSchedule::pop() always delivers a tuple, even when
// there's only one data field per item, as for us.
callWork(std::get<0>(work));
mQueue.close();
}
void LL::WorkQueue::callWork(const Work& work)
size_t LL::WorkSchedule::size()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
try
{
work();
}
catch (...)
{
// 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());
}
return mQueue.size();
}
void LL::WorkQueue::error(const std::string& msg)
bool LL::WorkSchedule::isClosed()
{
LL_ERRS("WorkQueue") << msg << LL_ENDL;
return mQueue.isClosed();
}
void LL::WorkQueue::checkCoroutine(const std::string& method)
bool LL::WorkSchedule::done()
{
// By convention, the default coroutine on each thread has an empty name
// string. See also LLCoros::logname().
if (LLCoros::getName().empty())
{
LLTHROW(Error("Do not call " + method + " from a thread's default coroutine"));
}
return mQueue.done();
}
bool LL::WorkSchedule::post(const Work& callable)
{
// Use TimePoint::clock::now() instead of TimePoint's representation of
// the epoch because this WorkSchedule may contain a mix of past-due
// TimedWork items and TimedWork items scheduled for the future. Sift this
// new item into the correct place.
return post(callable, TimePoint::clock::now());
}
bool LL::WorkSchedule::post(const Work& callable, const TimePoint& time)
{
return mQueue.pushIfOpen(TimedWork(time, callable));
}
bool LL::WorkSchedule::tryPost(const Work& callable)
{
return tryPost(callable, TimePoint::clock::now());
}
bool LL::WorkSchedule::tryPost(const Work& callable, const TimePoint& time)
{
return mQueue.tryPush(TimedWork(time, callable));
}
LL::WorkSchedule::Work LL::WorkSchedule::pop_()
{
return std::get<0>(mQueue.pop());
}
bool LL::WorkSchedule::tryPop_(Work& work)
{
return mQueue.tryPop(work);
}

View File

@ -15,6 +15,7 @@
#include "llcoros.h"
#include "llexception.h"
#include "llinstancetracker.h"
#include "llinstancetrackersubclass.h"
#include "threadsafeschedule.h"
#include <chrono>
#include <exception> // std::current_exception
@ -23,27 +24,23 @@
namespace LL
{
/*****************************************************************************
* WorkQueueBase: API for WorkQueue and WorkSchedule
*****************************************************************************/
/**
* A typical WorkQueue has a string name that can be used to find it.
*/
class WorkQueue: public LLInstanceTracker<WorkQueue, std::string>
class WorkQueueBase: public LLInstanceTracker<WorkQueueBase, std::string>
{
private:
using super = LLInstanceTracker<WorkQueue, std::string>;
using super = LLInstanceTracker<WorkQueueBase, std::string>;
public:
using Work = std::function<void()>;
private:
using Queue = ThreadSafeSchedule<Work>;
// helper for postEvery()
template <typename Rep, typename Period, typename CALLABLE>
class BackJack;
public:
using TimePoint = Queue::TimePoint;
using TimedWork = Queue::TimeTuple;
using Closed = Queue::Closed;
using Closed = LLThreadSafeQueueInterrupt;
// for runFor()
using TimePoint = std::chrono::steady_clock::time_point;
struct Error: public LLException
{
@ -51,18 +48,18 @@ namespace LL
};
/**
* You may omit the WorkQueue name, in which case a unique name is
* You may omit the WorkQueueBase 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);
WorkQueueBase(const std::string& name);
/**
* Since the point of WorkQueue is to pass work to some other worker
* thread(s) asynchronously, it's important that the WorkQueue continue
* to exist until the worker thread(s) have drained it. To communicate
* that it's time for them to quit, close() the queue.
* thread(s) asynchronously, it's important that it continue to exist
* until the worker thread(s) have drained it. To communicate that
* it's time for them to quit, close() the queue.
*/
void close();
virtual void close() = 0;
/**
* WorkQueue supports multiple producers and multiple consumers. In
@ -78,152 +75,57 @@ namespace LL
* * If you're the only consumer, noticing that size() > 0 is
* meaningful.
*/
size_t size();
virtual size_t size() = 0;
/// producer end: are we prevented from pushing any additional items?
bool isClosed();
virtual bool isClosed() = 0;
/// consumer end: are we done, is the queue entirely drained?
bool done();
virtual bool done() = 0;
/*---------------------- fire and forget API -----------------------*/
/// fire-and-forget, but at a particular (future?) time
template <typename CALLABLE>
void post(const TimePoint& time, CALLABLE&& callable)
{
// Defer reifying an arbitrary CALLABLE until we hit this or
// postIfOpen(). All other methods should accept CALLABLEs of
// arbitrary type to avoid multiple levels of std::function
// indirection.
mQueue.push(TimedWork(time, std::move(callable)));
}
/// fire-and-forget
template <typename CALLABLE>
void post(CALLABLE&& callable)
{
// We use TimePoint::clock::now() instead of TimePoint's
// representation of the epoch because this WorkQueue may contain
// a mix of past-due TimedWork items and TimedWork items scheduled
// for the future. Sift this new item into the correct place.
post(TimePoint::clock::now(), std::move(callable));
}
/**
* post work for a particular time, unless the queue is closed before
* we can post
*/
template <typename CALLABLE>
bool postIfOpen(const TimePoint& time, CALLABLE&& callable)
{
// Defer reifying an arbitrary CALLABLE until we hit this or
// post(). All other methods should accept CALLABLEs of arbitrary
// type to avoid multiple levels of std::function indirection.
return mQueue.pushIfOpen(TimedWork(time, std::move(callable)));
}
/**
* post work, unless the queue is closed before we can post
*/
template <typename CALLABLE>
bool postIfOpen(CALLABLE&& callable)
{
return postIfOpen(TimePoint::clock::now(), std::move(callable));
}
virtual bool post(const Work&) = 0;
/**
* Post work to be run at a specified time to another WorkQueue, which
* may or may not still exist and be open. Return true if we were able
* to post.
* post work, unless the queue is full
*/
template <typename CALLABLE>
static bool postMaybe(weak_t target, const TimePoint& time, CALLABLE&& callable);
virtual bool tryPost(const Work&) = 0;
/**
* Post work to another WorkQueue, which may or may not still exist
* and be open. Return true if we were able to post.
* and be open. Support any post() overload. Return true if we were
* able to post.
*/
template <typename CALLABLE>
static bool postMaybe(weak_t target, CALLABLE&& callable)
{
return postMaybe(target, TimePoint::clock::now(),
std::forward<CALLABLE>(callable));
}
/**
* Launch a callable returning bool that will trigger repeatedly at
* specified interval, until the callable returns false.
*
* If you need to signal that callable from outside, DO NOT bind a
* reference to a simple bool! That's not thread-safe. Instead, bind
* an LLCond variant, e.g. LLOneShotCond or LLBoolCond.
*/
template <typename Rep, typename Period, typename CALLABLE>
void postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable);
template <typename CALLABLE>
bool tryPost(CALLABLE&& callable)
{
return mQueue.tryPush(TimedWork(TimePoint::clock::now(), std::move(callable)));
}
template <typename... ARGS>
static bool postMaybe(weak_t target, ARGS&&... args);
/*------------------------- handshake API --------------------------*/
/**
* Post work to another WorkQueue to be run at a specified time,
* requesting a specific callback to be run on this WorkQueue on
* completion.
*
* Returns true if able to post, false if the other WorkQueue is
* inaccessible.
*/
// Apparently some Microsoft header file defines a macro CALLBACK? The
// natural template argument name CALLBACK produces very weird Visual
// Studio compile errors that seem utterly unrelated to this source
// code.
template <typename CALLABLE, typename FOLLOWUP>
bool postTo(weak_t target,
const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback);
/**
* Post work to another WorkQueue, requesting a specific callback to
* be run on this WorkQueue on completion.
* be run on this WorkQueue on completion. Optional final argument is
* TimePoint for WorkSchedule.
*
* Returns true if able to post, false if the other WorkQueue is
* inaccessible.
*/
template <typename CALLABLE, typename FOLLOWUP>
bool postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback)
{
return postTo(target, TimePoint::clock::now(),
std::move(callable), std::move(callback));
}
/**
* Post work to another WorkQueue to be run at a specified time,
* blocking the calling coroutine until then, returning the result to
* caller on completion.
*
* In general, we assume that each thread's default coroutine is busy
* servicing its WorkQueue or whatever. To try to prevent mistakes, we
* forbid calling waitForResult() from a thread's default coroutine.
*/
template <typename CALLABLE>
auto waitForResult(const TimePoint& time, CALLABLE&& callable);
template <typename CALLABLE, typename FOLLOWUP, typename... ARGS>
bool postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback,
ARGS&&... args);
/**
* Post work to another WorkQueue, blocking the calling coroutine
* until then, returning the result to caller on completion.
* until then, returning the result to caller on completion. Optional
* final argument is TimePoint for WorkSchedule.
*
* In general, we assume that each thread's default coroutine is busy
* servicing its WorkQueue or whatever. To try to prevent mistakes, we
* forbid calling waitForResult() from a thread's default coroutine.
*/
template <typename CALLABLE>
auto waitForResult(CALLABLE&& callable)
{
return waitForResult(TimePoint::clock::now(), std::move(callable));
}
template <typename CALLABLE, typename... ARGS>
auto waitForResult(CALLABLE&& callable, ARGS&&... args);
/*--------------------------- worker API ---------------------------*/
@ -270,7 +172,7 @@ namespace LL
*/
bool runUntil(const TimePoint& until);
private:
protected:
template <typename CALLABLE, typename FOLLOWUP>
static auto makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback);
/// general case: arbitrary C++ return type
@ -290,13 +192,170 @@ namespace LL
static void checkCoroutine(const std::string& method);
static void error(const std::string& msg);
static std::string makeName(const std::string& name);
void callWork(const Queue::DataTuple& work);
void callWork(const Work& work);
private:
virtual Work pop_() = 0;
virtual bool tryPop_(Work&) = 0;
};
/*****************************************************************************
* WorkQueue: no timestamped task support
*****************************************************************************/
class WorkQueue: public LLInstanceTrackerSubclass<WorkQueue, WorkQueueBase>
{
private:
using super = LLInstanceTrackerSubclass<WorkQueue, WorkQueueBase>;
public:
/**
* 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);
/**
* Since the point of WorkQueue is to pass work to some other worker
* thread(s) asynchronously, it's important that it continue to exist
* until the worker thread(s) have drained it. To communicate that
* it's time for them to quit, close() the queue.
*/
void close() override;
/**
* WorkQueue supports multiple producers and multiple consumers. In
* the general case it's misleading to test size(), since any other
* thread might change it the nanosecond the lock is released. On that
* basis, some might argue against publishing a size() method at all.
*
* But there are two specific cases in which a test based on size()
* might be reasonable:
*
* * If you're the only producer, noticing that size() == 0 is
* meaningful.
* * If you're the only consumer, noticing that size() > 0 is
* meaningful.
*/
size_t size() override;
/// producer end: are we prevented from pushing any additional items?
bool isClosed() override;
/// consumer end: are we done, is the queue entirely drained?
bool done() override;
/*---------------------- fire and forget API -----------------------*/
/**
* post work, unless the queue is closed before we can post
*/
bool post(const Work&) override;
/**
* post work, unless the queue is full
*/
bool tryPost(const Work&) override;
private:
using Queue = LLThreadSafeQueue<Work>;
Queue mQueue;
Work pop_() override;
bool tryPop_(Work&) override;
};
/*****************************************************************************
* WorkSchedule: add support for timestamped tasks
*****************************************************************************/
class WorkSchedule: public LLInstanceTrackerSubclass<WorkSchedule, WorkQueueBase>
{
private:
using super = LLInstanceTrackerSubclass<WorkSchedule, WorkQueueBase>;
using Queue = ThreadSafeSchedule<Work>;
// helper for postEvery()
template <typename Rep, typename Period, typename CALLABLE>
class BackJack;
public:
using TimePoint = Queue::TimePoint;
using TimedWork = Queue::TimeTuple;
/**
* 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);
/**
* Since the point of WorkSchedule is to pass work to some other worker
* thread(s) asynchronously, it's important that the WorkSchedule continue
* to exist until the worker thread(s) have drained it. To communicate
* that it's time for them to quit, close() the queue.
*/
void close() override;
/**
* WorkSchedule supports multiple producers and multiple consumers. In
* the general case it's misleading to test size(), since any other
* thread might change it the nanosecond the lock is released. On that
* basis, some might argue against publishing a size() method at all.
*
* But there are two specific cases in which a test based on size()
* might be reasonable:
*
* * If you're the only producer, noticing that size() == 0 is
* meaningful.
* * If you're the only consumer, noticing that size() > 0 is
* meaningful.
*/
size_t size() override;
/// producer end: are we prevented from pushing any additional items?
bool isClosed() override;
/// consumer end: are we done, is the queue entirely drained?
bool done() override;
/*---------------------- fire and forget API -----------------------*/
/**
* post work, unless the queue is closed before we can post
*/
bool post(const Work& callable) override;
/**
* post work for a particular time, unless the queue is closed before
* we can post
*/
bool post(const Work& callable, const TimePoint& time);
/**
* post work, unless the queue is full
*/
bool tryPost(const Work& callable) override;
/**
* post work for a particular time, unless the queue is full
*/
bool tryPost(const Work& callable, const TimePoint& time);
/**
* Launch a callable returning bool that will trigger repeatedly at
* specified interval, until the callable returns false.
*
* If you need to signal that callable from outside, DO NOT bind a
* reference to a simple bool! That's not thread-safe. Instead, bind
* an LLCond variant, e.g. LLOneShotCond or LLBoolCond.
*/
template <typename Rep, typename Period, typename CALLABLE>
bool postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable);
private:
Queue mQueue;
Work pop_() override;
bool tryPop_(Work&) override;
};
/**
* BackJack is, in effect, a hand-rolled lambda, binding a WorkQueue, a
* BackJack is, in effect, a hand-rolled lambda, binding a WorkSchedule, a
* CALLABLE that returns bool, a TimePoint and an interval at which to
* relaunch it. As long as the callable continues returning true, BackJack
* keeps resubmitting it to the target WorkQueue.
@ -305,7 +364,7 @@ namespace LL
// class method gets its own 'this' pointer -- which we need to resubmit
// the whole BackJack callable.
template <typename Rep, typename Period, typename CALLABLE>
class WorkQueue::BackJack
class WorkSchedule::BackJack
{
public:
// bind the desired data
@ -319,9 +378,10 @@ namespace LL
mCallable(std::move(callable))
{}
// Call by target WorkQueue -- note that although WE require a
// callable returning bool, WorkQueue wants a void callable. We
// consume the bool.
// This operator() method, called by target WorkSchedule, is what
// makes this object a Work item. Although WE require a callable
// returning bool, WorkSchedule wants a void callable. We consume the
// bool.
void operator()()
{
// If mCallable() throws an exception, don't catch it here: if it
@ -337,7 +397,7 @@ namespace LL
// register our intent to fire at exact mIntervals.
mStart += mInterval;
// We're being called at this moment by the target WorkQueue.
// We're being called at this moment by the target WorkSchedule.
// Assume it still exists, rather than checking the result of
// lock().
// Resubmit the whole *this callable: that's why we're a class
@ -345,14 +405,10 @@ namespace LL
// move-only callable; but naturally this statement must be
// the last time we reference this instance, which may become
// moved-from.
try
{
mTarget.lock()->post(mStart, std::move(*this));
}
catch (const Closed&)
{
// Once this queue is closed, oh well, just stop
}
auto target{ std::dynamic_pointer_cast<WorkSchedule>(mTarget.lock()) };
// Discard bool return: once this queue is closed, oh well,
// just stop
target->post(std::move(*this), mStart);
}
}
@ -364,7 +420,7 @@ namespace LL
};
template <typename Rep, typename Period, typename CALLABLE>
void WorkQueue::postEvery(const std::chrono::duration<Rep, Period>& interval,
bool WorkSchedule::postEvery(const std::chrono::duration<Rep, Period>& interval,
CALLABLE&& callable)
{
if (interval.count() <= 0)
@ -381,14 +437,14 @@ namespace LL
// Instantiate and post a suitable BackJack, binding a weak_ptr to
// self, the current time, the desired interval and the desired
// callable.
post(
return post(
BackJack<Rep, Period, CALLABLE>(
getWeak(), TimePoint::clock::now(), interval, std::move(callable)));
}
/// general case: arbitrary C++ return type
template <typename CALLABLE, typename FOLLOWUP, typename RETURNTYPE>
struct WorkQueue::MakeReplyLambda
struct WorkQueueBase::MakeReplyLambda
{
auto operator()(CALLABLE&& callable, FOLLOWUP&& callback)
{
@ -409,7 +465,7 @@ namespace LL
/// specialize for CALLABLE returning void
template <typename CALLABLE, typename FOLLOWUP>
struct WorkQueue::MakeReplyLambda<CALLABLE, FOLLOWUP, void>
struct WorkQueueBase::MakeReplyLambda<CALLABLE, FOLLOWUP, void>
{
auto operator()(CALLABLE&& callable, FOLLOWUP&& callback)
{
@ -421,16 +477,16 @@ namespace LL
};
template <typename CALLABLE, typename FOLLOWUP>
auto WorkQueue::makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback)
auto WorkQueueBase::makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback)
{
return MakeReplyLambda<CALLABLE, FOLLOWUP,
decltype(std::forward<CALLABLE>(callable)())>()
(std::move(callable), std::move(callback));
}
template <typename CALLABLE, typename FOLLOWUP>
bool WorkQueue::postTo(weak_t target,
const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback)
template <typename CALLABLE, typename FOLLOWUP, typename... ARGS>
bool WorkQueueBase::postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback,
ARGS&&... args)
{
LL_PROFILE_ZONE_SCOPED;
// We're being asked to post to the WorkQueue at target.
@ -443,13 +499,12 @@ namespace LL
// Here we believe target WorkQueue still exists. Post to it a
// lambda that packages our callable, our callback and a weak_ptr
// to this originating WorkQueue.
tptr->post(
time,
return tptr->post(
[reply = super::getWeak(),
callable = std::move(callable),
callback = std::move(callback)]
()
mutable {
() mutable
{
// Use postMaybe() below in case this originating WorkQueue
// has been closed or destroyed. Remember, the outer lambda is
// now running on a thread servicing the target WorkQueue, and
@ -472,44 +527,34 @@ namespace LL
// originating WorkQueue. Once there, rethrow it.
[exc = std::current_exception()](){ std::rethrow_exception(exc); });
}
});
// looks like we were able to post()
return true;
},
// if caller passed a TimePoint, pass it along to post()
std::forward<ARGS>(args)...);
}
template <typename CALLABLE>
bool WorkQueue::postMaybe(weak_t target, const TimePoint& time, CALLABLE&& callable)
template <typename... ARGS>
bool WorkQueueBase::postMaybe(weak_t target, ARGS&&... args)
{
LL_PROFILE_ZONE_SCOPED;
// target is a weak_ptr: have to lock it to check it
auto tptr = target.lock();
if (tptr)
{
try
{
tptr->post(time, std::forward<CALLABLE>(callable));
// we were able to post()
return true;
return tptr->post(std::forward<ARGS>(args)...);
}
catch (const Closed&)
{
// target WorkQueue still exists, but is Closed
}
}
// either target no longer exists, or its WorkQueue is Closed
// target no longer exists
return false;
}
/// general case: arbitrary C++ return type
template <typename CALLABLE, typename RETURNTYPE>
struct WorkQueue::WaitForResult
struct WorkQueueBase::WaitForResult
{
auto operator()(WorkQueue* self, const TimePoint& time, CALLABLE&& callable)
template <typename... ARGS>
auto operator()(WorkQueueBase* self, CALLABLE&& callable, ARGS&&... args)
{
LLCoros::Promise<RETURNTYPE> promise;
self->post(
time,
bool posted = self->post(
// We dare to bind a reference to Promise because it's
// specifically designed for cross-thread communication.
[&promise, callable = std::move(callable)]()
@ -523,7 +568,13 @@ namespace LL
{
promise.set_exception(std::current_exception());
}
});
},
// if caller passed a TimePoint, pass it to post()
std::forward<ARGS>(args)...);
if (! posted)
{
LLTHROW(WorkQueueBase::Closed());
}
auto future{ LLCoros::getFuture(promise) };
// now, on the calling thread, wait for that result
LLCoros::TempStatus st("waiting for WorkQueue::waitForResult()");
@ -533,13 +584,13 @@ namespace LL
/// specialize for CALLABLE returning void
template <typename CALLABLE>
struct WorkQueue::WaitForResult<CALLABLE, void>
struct WorkQueueBase::WaitForResult<CALLABLE, void>
{
void operator()(WorkQueue* self, const TimePoint& time, CALLABLE&& callable)
template <typename... ARGS>
void operator()(WorkQueueBase* self, CALLABLE&& callable, ARGS&&... args)
{
LLCoros::Promise<void> promise;
self->post(
time,
bool posted = self->post(
// &promise is designed for cross-thread access
[&promise, callable = std::move(callable)]()
mutable {
@ -552,7 +603,13 @@ namespace LL
{
promise.set_exception(std::current_exception());
}
});
},
// if caller passed a TimePoint, pass it to post()
std::forward<ARGS>(args)...);
if (! posted)
{
LLTHROW(WorkQueueBase::Closed());
}
auto future{ LLCoros::getFuture(promise) };
// block until set_value()
LLCoros::TempStatus st("waiting for void WorkQueue::waitForResult()");
@ -560,13 +617,13 @@ namespace LL
}
};
template <typename CALLABLE>
auto WorkQueue::waitForResult(const TimePoint& time, CALLABLE&& callable)
template <typename CALLABLE, typename... ARGS>
auto WorkQueueBase::waitForResult(CALLABLE&& callable, ARGS&&... args)
{
checkCoroutine("waitForResult()");
// derive callable's return type so we can specialize for void
return WaitForResult<CALLABLE, decltype(std::forward<CALLABLE>(callable)())>()
(this, time, std::forward<CALLABLE>(callable));
(this, std::forward<CALLABLE>(callable), std::forward<ARGS>(args)...);
}
} // namespace LL

View File

@ -113,6 +113,7 @@ void HttpLibcurl::shutdown()
void HttpLibcurl::start(int policy_count)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT);
llassert_always(! mMultiHandles); // One-time call only
@ -143,6 +144,7 @@ void HttpLibcurl::start(int policy_count)
// sleep otherwise ask for a normal polling interval.
HttpService::ELoopSpeed HttpLibcurl::processTransport()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP);
// Give libcurl some cycles to do I/O & callbacks
@ -168,6 +170,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
CURLMcode status(CURLM_CALL_MULTI_PERFORM);
do
{
LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("httppt - curl_multi_perform");
running = 0;
status = curl_multi_perform(mMultiHandles[policy_class], &running);
}
@ -176,11 +179,13 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
// Run completion on anything done
CURLMsg * msg(NULL);
int msgs_in_queue(0);
{
LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("httppt - curl_multi_info_read");
while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue)))
{
if (CURLMSG_DONE == msg->msg)
{
CURL * handle(msg->easy_handle);
CURL* handle(msg->easy_handle);
CURLcode result(msg->data.result);
completeRequest(mMultiHandles[policy_class], handle, result);
@ -202,6 +207,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
msgs_in_queue = 0;
}
}
}
if (! mActiveOps.empty())
{
@ -214,6 +220,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
// Caller has provided us with a ref count on op.
void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(op->mReqPolicy < mPolicyCount);
llassert_always(mMultiHandles[op->mReqPolicy] != NULL);
@ -257,6 +264,7 @@ void HttpLibcurl::addOp(const HttpOpRequest::ptr_t &op)
// method to kill the request.
bool HttpLibcurl::cancel(HttpHandle handle)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op = HttpOpRequest::fromHandle<HttpOpRequest>(handle);
active_set_t::iterator it(mActiveOps.find(op));
if (mActiveOps.end() == it)
@ -282,6 +290,7 @@ bool HttpLibcurl::cancel(HttpHandle handle)
// op to the reply queue with refcount intact.
void HttpLibcurl::cancelRequest(const HttpOpRequest::ptr_t &op)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// Deactivate request
op->mCurlActive = false;
@ -308,6 +317,7 @@ void HttpLibcurl::cancelRequest(const HttpOpRequest::ptr_t &op)
// Keep them synchronized as necessary.
bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpHandle ophandle(NULL);
CURLcode ccode(CURLE_OK);
@ -445,6 +455,7 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) const
void HttpLibcurl::policyUpdated(int policy_class)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (policy_class < 0 || policy_class >= mPolicyCount || ! mMultiHandles)
{
return;

View File

@ -62,7 +62,6 @@ HttpOperation::HttpOperation():
mReplyQueue(),
mUserHandler(),
mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
mReqPriority(0U),
mTracing(HTTP_TRACE_OFF),
mMyHandle(LLCORE_HTTP_HANDLE_INVALID)
{

View File

@ -181,7 +181,6 @@ protected:
public:
// Request Data
HttpRequest::policy_t mReqPolicy;
HttpRequest::priority_t mReqPriority;
// Reply Data
HttpStatus mStatus;

View File

@ -200,6 +200,7 @@ HttpOpRequest::~HttpOpRequest()
void HttpOpRequest::stageFromRequest(HttpService * service)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this()));
service->getPolicy().addOp(self); // transfers refcount
}
@ -207,6 +208,7 @@ void HttpOpRequest::stageFromRequest(HttpService * service)
void HttpOpRequest::stageFromReady(HttpService * service)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t self(boost::dynamic_pointer_cast<HttpOpRequest>(shared_from_this()));
service->getTransport().addOp(self); // transfers refcount
}
@ -214,6 +216,7 @@ void HttpOpRequest::stageFromReady(HttpService * service)
void HttpOpRequest::stageFromActive(HttpService * service)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (mReplyLength)
{
// If non-zero, we received and processed a Content-Range
@ -250,6 +253,7 @@ void HttpOpRequest::stageFromActive(HttpService * service)
void HttpOpRequest::visitNotifier(HttpRequest * request)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (mUserHandler)
{
HttpResponse * response = new HttpResponse();
@ -292,6 +296,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
HttpStatus HttpOpRequest::cancel()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED);
addAsReply();
@ -301,12 +306,12 @@ HttpStatus HttpOpRequest::cancel()
HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, NULL, options, headers);
mReqMethod = HOR_GET;
return HttpStatus();
@ -314,14 +319,14 @@ HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
size_t offset,
size_t len,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, NULL, options, headers);
mReqMethod = HOR_GET;
mReqOffset = offset;
mReqLength = len;
@ -335,13 +340,13 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, body, options, headers);
mReqMethod = HOR_POST;
return HttpStatus();
@ -349,13 +354,13 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, body, options, headers);
mReqMethod = HOR_PUT;
return HttpStatus();
@ -363,12 +368,12 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupDelete(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, NULL, options, headers);
mReqMethod = HOR_DELETE;
return HttpStatus();
@ -376,13 +381,13 @@ HttpStatus HttpOpRequest::setupDelete(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupPatch(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
setupCommon(policy_id, priority, url, body, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, body, options, headers);
mReqMethod = HOR_PATCH;
return HttpStatus();
@ -390,12 +395,12 @@ HttpStatus HttpOpRequest::setupPatch(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupCopy(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t &headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, NULL, options, headers);
mReqMethod = HOR_COPY;
return HttpStatus();
@ -403,12 +408,12 @@ HttpStatus HttpOpRequest::setupCopy(HttpRequest::policy_t policy_id,
HttpStatus HttpOpRequest::setupMove(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t &headers)
{
setupCommon(policy_id, priority, url, NULL, options, headers);
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
setupCommon(policy_id, url, NULL, options, headers);
mReqMethod = HOR_MOVE;
return HttpStatus();
@ -416,15 +421,14 @@ HttpStatus HttpOpRequest::setupMove(HttpRequest::policy_t policy_id,
void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
mProcFlags = 0U;
mReqPolicy = policy_id;
mReqPriority = priority;
mReqURL = url;
if (body)
{
@ -465,6 +469,7 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
// *TODO: Move this to _httplibcurl where it belongs.
HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// Scrub transport and result data for retried op case
mCurlActive = false;
mCurlHandle = NULL;
@ -773,6 +778,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (! op->mReplyBody)
@ -788,6 +794,7 @@ size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void
size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (! op->mReqBody)
@ -819,6 +826,7 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void
int HttpOpRequest::seekCallback(void *userdata, curl_off_t offset, int origin)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (!op->mReqBody)
@ -850,6 +858,7 @@ int HttpOpRequest::seekCallback(void *userdata, curl_off_t offset, int origin)
size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
static const char status_line[] = "HTTP/";
static const size_t status_line_len = sizeof(status_line) - 1;
static const char con_ran_line[] = "content-range";
@ -999,6 +1008,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userdata)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
if (op->mCallbackSSLVerify)
@ -1025,6 +1035,7 @@ CURLcode HttpOpRequest::curlSslCtxCallback(CURL *curl, void *sslctx, void *userd
int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(param));
if (op->mCallbackSSLVerify)
@ -1037,6 +1048,7 @@ int HttpOpRequest::sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param)
int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpOpRequest::ptr_t op(HttpOpRequest::fromHandle<HttpOpRequest>(userdata));
std::string safe_line;

View File

@ -105,13 +105,11 @@ public:
/// Threading: called by application thread
///
HttpStatus setupGet(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
size_t offset,
size_t len,
@ -119,40 +117,34 @@ public:
const HttpHeaders::ptr_t & headers);
HttpStatus setupPost(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPut(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupDelete(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupPatch(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupCopy(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
HttpStatus setupMove(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers);
@ -172,7 +164,6 @@ protected:
// Threading: called by application thread
//
void setupCommon(HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -239,19 +230,6 @@ public:
/// HttpOpRequestCompare isn't an operation but a uniform comparison
/// functor for STL containers that order by priority. Mainly
/// used for the ready queue container but defined here.
class HttpOpRequestCompare
{
public:
bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs)
{
return lhs->mReqPriority > rhs->mReqPriority;
}
}; // end class HttpOpRequestCompare
// ---------------------------------------
// Free functions
// ---------------------------------------

View File

@ -24,6 +24,7 @@
* $/LicenseInfo$
*/
#if 0 // DEPRECATED
#include "_httpopsetpriority.h"
#include "httpresponse.h"
@ -61,3 +62,5 @@ void HttpOpSetPriority::stageFromRequest(HttpService * service)
} // end namespace LLCore
#endif

View File

@ -27,7 +27,7 @@
#ifndef _LLCORE_HTTP_SETPRIORITY_H_
#define _LLCORE_HTTP_SETPRIORITY_H_
#if 0 // DEPRECATED
#include "httpcommon.h"
#include "httprequest.h"
#include "_httpoperation.h"
@ -49,7 +49,7 @@ namespace LLCore
class HttpOpSetPriority : public HttpOperation
{
public:
HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority);
HttpOpSetPriority(HttpHandle handle);
virtual ~HttpOpSetPriority();
@ -63,10 +63,10 @@ public:
protected:
// Request Data
HttpHandle mHandle;
HttpRequest::priority_t mPriority;
}; // end class HttpOpSetPriority
} // end namespace LLCore
#endif
#endif // _LLCORE_HTTP_SETPRIORITY_H_

View File

@ -330,37 +330,6 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
return result;
}
bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
{
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
{
ClassState & state(*mClasses[policy_class]);
// We don't scan retry queue because a priority change there
// is meaningless. The request will be issued based on retry
// intervals not priority value, which is now moot.
// Scan ready queue for requests that match policy
HttpReadyQueue::container_type & c(state.mReadyQueue.get_container());
for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;)
{
HttpReadyQueue::container_type::iterator cur(iter++);
if ((*cur)->getHandle() == handle)
{
HttpOpRequest::ptr_t op(*cur);
c.erase(cur); // All iterators are now invalidated
op->mReqPriority = priority;
state.mReadyQueue.push(op); // Re-insert using adapter class
return true;
}
}
}
return false;
}
bool HttpPolicy::cancel(HttpHandle handle)
{
for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)

View File

@ -110,12 +110,6 @@ public:
/// Threading: called by worker thread
void retryOp(const opReqPtr_t &);
/// Attempt to change the priority of an earlier request.
/// Request that Shadows HttpService's method
///
/// Threading: called by worker thread
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
/// Attempt to cancel a previous request.
/// Shadows HttpService's method as well
///

View File

@ -80,6 +80,7 @@ HttpService::HttpService()
HttpService::~HttpService()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
mExitRequested = 1U;
if (RUNNING == sState)
{
@ -131,6 +132,7 @@ HttpService::~HttpService()
void HttpService::init(HttpRequestQueue * queue)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(! sInstance);
llassert_always(NOT_INITIALIZED == sState);
sInstance = new HttpService();
@ -145,6 +147,7 @@ void HttpService::init(HttpRequestQueue * queue)
void HttpService::term()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (sInstance)
{
if (RUNNING == sState && sInstance->mThread)
@ -196,6 +199,7 @@ bool HttpService::isStopped()
/// Threading: callable by consumer thread *once*.
void HttpService::startThread()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
llassert_always(! mThread || STOPPED == sState);
llassert_always(INITIALIZED == sState || STOPPED == sState);
@ -220,22 +224,6 @@ void HttpService::stopRequested()
}
/// Threading: callable by worker thread.
bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
{
bool found(false);
// Skip the request queue as we currently don't leave earlier
// requests sitting there. Start with the ready queue...
found = mPolicy->changePriority(handle, priority);
// If not there, we could try the transport/active queue but priority
// doesn't really have much effect there so we don't waste cycles.
return found;
}
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///
@ -244,6 +232,7 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio
/// Threading: callable by worker thread.
bool HttpService::cancel(HttpHandle handle)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
bool canceled(false);
// Request can't be on request queue so skip that.
@ -264,6 +253,7 @@ bool HttpService::cancel(HttpHandle handle)
/// Threading: callable by worker thread.
void HttpService::shutdown()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
// Disallow future enqueue of requests
mRequestQueue->stopQueue();
@ -293,6 +283,8 @@ void HttpService::shutdown()
// requested to stop.
void HttpService::threadRun(LLCoreInt::HttpThread * thread)
{
LL_PROFILER_SET_THREAD_NAME("HttpService");
boost::this_thread::disable_interruption di;
LLThread::registerThreadID();
@ -300,6 +292,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread)
ELoopSpeed loop(REQUEST_SLEEP);
while (! mExitRequested)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
try
{
loop = processRequestQueue(loop);
@ -344,6 +337,7 @@ void HttpService::threadRun(LLCoreInt::HttpThread * thread)
HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpRequestQueue::OpContainer ops;
const bool wait_for_req(REQUEST_SLEEP == loop);
@ -384,6 +378,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
|| opt >= HttpRequest::PO_LAST // ditto
|| (! sOptionDesc[opt].mIsLong) // datatype is long
@ -416,6 +411,7 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
std::string * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
@ -443,6 +439,7 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
@ -472,6 +469,7 @@ HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
long value, long * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
@ -517,6 +515,7 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
const std::string & value, std::string * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range
@ -548,6 +547,7 @@ HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequ
HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
HttpRequest::policyCallback_t value, HttpRequest::policyCallback_t * ret_value)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
if (opt < HttpRequest::PO_CONNECTION_LIMIT // option must be in range

View File

@ -146,15 +146,6 @@ public:
/// Threading: callable by worker thread.
void shutdown();
/// Try to find the given request handle on any of the request
/// queues and reset the priority (and queue position) of the
/// request if found.
///
/// @return True if the request was found somewhere.
///
/// Threading: callable by worker thread.
bool changePriority(HttpHandle handle, HttpRequest::priority_t priority);
/// Try to find the given request handle on any of the request
/// queues and cancel the operation.
///

View File

@ -469,11 +469,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions::ptr_t & o
LLCore::HttpHandle handle;
if (offset || length)
{
handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
handle = hr->requestGetByteRange(0, buffer, offset, length, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
else
{
handle = hr->requestGet(0, 0, buffer, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
handle = hr->requestGet(0, buffer, opt, mHeaders, LLCore::HttpHandler::ptr_t(this, NoOpDeletor));
}
if (! handle)
{

View File

@ -32,7 +32,6 @@
#include "_httppolicy.h"
#include "_httpoperation.h"
#include "_httpoprequest.h"
#include "_httpopsetpriority.h"
#include "_httpopcancel.h"
#include "_httpopsetget.h"
@ -183,16 +182,16 @@ HttpStatus HttpRequest::getStatus() const
HttpHandle HttpRequest::requestGet(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGet(policy_id, priority, url, options, headers)))
if (! (status = op->setupGet(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -210,7 +209,6 @@ HttpHandle HttpRequest::requestGet(policy_t policy_id,
HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
priority_t priority,
const std::string & url,
size_t offset,
size_t len,
@ -218,10 +216,11 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
const HttpHeaders::ptr_t & headers,
HttpHandler::ptr_t user_handler)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers)))
if (! (status = op->setupGetByteRange(policy_id, url, offset, len, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -239,7 +238,6 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id,
HttpHandle HttpRequest::requestPost(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -249,7 +247,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (! (status = op->setupPost(policy_id, priority, url, body, options, headers)))
if (! (status = op->setupPost(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -267,7 +265,6 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id,
HttpHandle HttpRequest::requestPut(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -277,7 +274,7 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (! (status = op->setupPut(policy_id, priority, url, body, options, headers)))
if (! (status = op->setupPut(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -294,7 +291,6 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id,
}
HttpHandle HttpRequest::requestDelete(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -303,7 +299,7 @@ HttpHandle HttpRequest::requestDelete(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupDelete(policy_id, priority, url, options, headers)))
if (!(status = op->setupDelete(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -320,7 +316,6 @@ HttpHandle HttpRequest::requestDelete(policy_t policy_id,
}
HttpHandle HttpRequest::requestPatch(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -330,7 +325,7 @@ HttpHandle HttpRequest::requestPatch(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupPatch(policy_id, priority, url, body, options, headers)))
if (!(status = op->setupPatch(policy_id, url, body, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -347,7 +342,6 @@ HttpHandle HttpRequest::requestPatch(policy_t policy_id,
}
HttpHandle HttpRequest::requestCopy(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -356,7 +350,7 @@ HttpHandle HttpRequest::requestCopy(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op(new HttpOpRequest());
if (!(status = op->setupCopy(policy_id, priority, url, options, headers)))
if (!(status = op->setupCopy(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -374,7 +368,6 @@ HttpHandle HttpRequest::requestCopy(policy_t policy_id,
}
HttpHandle HttpRequest::requestMove(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -383,7 +376,7 @@ HttpHandle HttpRequest::requestMove(policy_t policy_id,
HttpStatus status;
HttpOpRequest::ptr_t op (new HttpOpRequest());
if (!(status = op->setupMove(policy_id, priority, url, options, headers)))
if (!(status = op->setupMove(policy_id, url, options, headers)))
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
@ -483,24 +476,6 @@ HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler::ptr_t use
}
HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority,
HttpHandler::ptr_t handler)
{
HttpStatus status;
HttpOperation::ptr_t op (new HttpOpSetPriority(request, priority));
op->setReplyPath(mReplyQueue, handler);
if (! (status = mRequestQueue->addOp(op))) // transfers refcount
{
mLastReqStatus = status;
return LLCORE_HTTP_HANDLE_INVALID;
}
mLastReqStatus = status;
return op->getHandle();
}
// ====================================
// Utility Methods
// ====================================

View File

@ -95,7 +95,6 @@ private:
public:
typedef unsigned int policy_t;
typedef unsigned int priority_t;
typedef boost::shared_ptr<HttpRequest> ptr_t;
typedef boost::weak_ptr<HttpRequest> wptr_t;
@ -316,8 +315,6 @@ public:
///
/// @param policy_id Default or user-defined policy class under
/// which this request is to be serviced.
/// @param priority Standard priority scheme inherited from
/// Indra code base (U32-type scheme).
/// @param url URL with any encoded query parameters to
/// be accessed.
/// @param options Optional instance of an HttpOptions object
@ -346,7 +343,6 @@ public:
/// case, @see getStatus() will return more info.
///
HttpHandle requestGet(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -377,7 +373,6 @@ public:
/// - Referer:
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param offset Offset of first byte into resource to be returned.
/// @param len Count of bytes to be returned
@ -387,7 +382,6 @@ public:
/// @return "
///
HttpHandle requestGetByteRange(policy_t policy_id,
priority_t priority,
const std::string & url,
size_t offset,
size_t len,
@ -418,7 +412,6 @@ public:
/// - Expect:
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
@ -429,7 +422,6 @@ public:
/// @return "
///
HttpHandle requestPost(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -459,7 +451,6 @@ public:
/// - Content-Type:
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
@ -470,7 +461,6 @@ public:
/// @return "
///
HttpHandle requestPut(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -483,7 +473,6 @@ public:
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
@ -491,7 +480,6 @@ public:
/// @return "
///
HttpHandle requestDelete(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -502,7 +490,6 @@ public:
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param body Byte stream to be sent as the body. No
/// further encoding or escaping will be done
@ -513,7 +500,6 @@ public:
/// @return "
///
HttpHandle requestPatch(policy_t policy_id,
priority_t priority,
const std::string & url,
BufferArray * body,
const HttpOptions::ptr_t & options,
@ -525,7 +511,6 @@ public:
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
@ -533,7 +518,6 @@ public:
/// @return "
///
HttpHandle requestCopy(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -544,7 +528,6 @@ public:
/// encoding and communicating the content types.
///
/// @param policy_id @see requestGet()
/// @param priority "
/// @param url "
/// @param options @see requestGet()K(optional)
/// @param headers "
@ -552,7 +535,6 @@ public:
/// @return "
///
HttpHandle requestMove(policy_t policy_id,
priority_t priority,
const std::string & url,
const HttpOptions::ptr_t & options,
const HttpHeaders::ptr_t & headers,
@ -593,18 +575,6 @@ public:
HttpHandle requestCancel(HttpHandle request, HttpHandler::ptr_t);
/// Request that a previously-issued request be reprioritized.
/// The status of whether the change itself succeeded arrives
/// via notification.
///
/// @param request Handle of previously-issued request to
/// be changed.
/// @param priority New priority value.
/// @param handler @see requestGet()
/// @return "
///
HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler::ptr_t handler);
/// @}
/// @name UtilityMethods

View File

@ -614,7 +614,6 @@ void HttpRequestTestObjectType::test<7>()
// Issue a GET that can't connect
mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
"http://127.0.0.1:2/nothing/here",
0,
0,
@ -716,7 +715,6 @@ void HttpRequestTestObjectType::test<8>()
// Issue a GET that *can* connect
mStatus = HttpStatus(200);
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
HttpOptions::ptr_t(),
HttpHeaders::ptr_t(),
@ -812,7 +810,6 @@ void HttpRequestTestObjectType::test<9>()
// Issue a GET that *can* connect
mStatus = HttpStatus(200);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
0,
0,
@ -913,7 +910,6 @@ void HttpRequestTestObjectType::test<10>()
body->append(body_text, strlen(body_text));
mStatus = HttpStatus(200);
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
body,
HttpOptions::ptr_t(),
@ -1020,7 +1016,6 @@ void HttpRequestTestObjectType::test<11>()
body->append(body_text, strlen(body_text));
mStatus = HttpStatus(200);
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
body,
HttpOptions::ptr_t(),
@ -1127,7 +1122,6 @@ void HttpRequestTestObjectType::test<12>()
// Issue a GET that *can* connect
mStatus = HttpStatus(200);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
0,
0,
@ -1240,7 +1234,6 @@ void HttpRequestTestObjectType::test<13>()
regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
0,
0,
@ -1346,7 +1339,6 @@ void HttpRequestTestObjectType::test<14>()
// Issue a GET that sleeps
mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
0,
0,
@ -1454,7 +1446,6 @@ void HttpRequestTestObjectType::test<15>()
mStatus = HttpStatus(200);
handler.mCheckContentType = "application/llsd+xml";
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base,
HttpOptions::ptr_t(),
HttpHeaders::ptr_t(),
@ -1609,7 +1600,6 @@ void HttpRequestTestObjectType::test<16>()
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
options,
HttpHeaders::ptr_t(),
@ -1684,7 +1674,6 @@ void HttpRequestTestObjectType::test<16>()
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
0,
47,
@ -1863,7 +1852,6 @@ void HttpRequestTestObjectType::test<17>()
boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
boost::regex(".*chunked.*", boost::regex::icase)));
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
ba,
options,
@ -2049,7 +2037,6 @@ void HttpRequestTestObjectType::test<18>()
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
ba,
options,
@ -2249,7 +2236,6 @@ void HttpRequestTestObjectType::test<19>()
boost::regex("X-Reflect-content-encoding", boost::regex::icase),
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
options,
headers,
@ -2457,7 +2443,6 @@ void HttpRequestTestObjectType::test<20>()
boost::regex(".*", boost::regex::icase)));
HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
ba,
options,
@ -2666,7 +2651,6 @@ void HttpRequestTestObjectType::test<21>()
boost::regex("X-Reflect-content-type", boost::regex::icase),
boost::regex("text/html", boost::regex::icase)));
HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + "reflect/",
ba,
options,
@ -2797,7 +2781,6 @@ void HttpRequestTestObjectType::test<22>()
char buffer[128];
sprintf(buffer, "/bug2295/%d/", i);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + buffer,
0,
25,
@ -2829,7 +2812,6 @@ void HttpRequestTestObjectType::test<22>()
char buffer[128];
sprintf(buffer, "/bug2295/00000012/%d/", i);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + buffer,
0,
25,
@ -2861,7 +2843,6 @@ void HttpRequestTestObjectType::test<22>()
char buffer[128];
sprintf(buffer, "/bug2295/inv_cont_range/%d/", i);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url_base + buffer,
0,
25,
@ -2984,7 +2965,6 @@ void HttpRequestTestObjectType::test<23>()
std::ostringstream url;
url << url_base << i << "/";
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
0U,
url.str(),
0,
0,

View File

@ -418,7 +418,7 @@ bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
LL_INFOS("CRASHREPORT") << "POST crash data to " << host << LL_ENDL;
LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID, 0,
LLCore::HttpHandle handle = LLCoreHttpUtil::requestPostWithLLSD(httpRequest.get(), LLCore::HttpRequest::DEFAULT_POLICY_ID,
host, data, httpOpts, LLCore::HttpHeaders::ptr_t(), LLCore::HttpHandler::ptr_t(new LLCrashLoggerHandler));
if (handle == LLCORE_HTTP_HANDLE_INVALID)

View File

@ -221,6 +221,7 @@ const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at)
{ LLAssetType::AT_PERSON, "PERSON" },
{ LLAssetType::AT_MESH, "MESH" },
{ LLAssetType::AT_SETTINGS, "SETTINGS" },
{ LLAssetType::AT_MATERIAL, "MATERIAL" },
{ LLAssetType::AT_UNKNOWN, "UNKNOWN" }
};

View File

@ -45,8 +45,7 @@ void LLLFSThread::initClass(bool local_is_threaded)
//static
S32 LLLFSThread::updateClass(U32 ms_elapsed)
{
sLocal->update((F32)ms_elapsed);
return sLocal->getPending();
return sLocal->update((F32)ms_elapsed);
}
//static
@ -58,6 +57,7 @@ void LLLFSThread::cleanupClass()
{
sLocal->update(0);
}
sLocal->shutdown();
delete sLocal;
sLocal = NULL;
}
@ -65,8 +65,7 @@ void LLLFSThread::cleanupClass()
//----------------------------------------------------------------------------
LLLFSThread::LLLFSThread(bool threaded) :
LLQueuedThread("LFS", threaded),
mPriorityCounter(PRIORITY_LOWBITS)
LLQueuedThread("LFS", threaded)
{
if(!mLocalAPRFilePoolp)
{
@ -84,14 +83,12 @@ LLLFSThread::~LLLFSThread()
LLLFSThread::handle_t LLLFSThread::read(const std::string& filename, /* Flawfinder: ignore */
U8* buffer, S32 offset, S32 numbytes,
Responder* responder, U32 priority)
Responder* responder)
{
LL_PROFILE_ZONE_SCOPED;
handle_t handle = generateHandle();
if (priority == 0) priority = PRIORITY_NORMAL | priorityCounter();
else if (priority < PRIORITY_LOW) priority |= PRIORITY_LOW; // All reads are at least PRIORITY_LOW
Request* req = new Request(this, handle, priority,
Request* req = new Request(this, handle,
FILE_READ, filename,
buffer, offset, numbytes,
responder);
@ -107,13 +104,12 @@ LLLFSThread::handle_t LLLFSThread::read(const std::string& filename, /* Flawfind
LLLFSThread::handle_t LLLFSThread::write(const std::string& filename,
U8* buffer, S32 offset, S32 numbytes,
Responder* responder, U32 priority)
Responder* responder)
{
LL_PROFILE_ZONE_SCOPED;
handle_t handle = generateHandle();
if (priority == 0) priority = PRIORITY_LOW | priorityCounter();
Request* req = new Request(this, handle, priority,
Request* req = new Request(this, handle,
FILE_WRITE, filename,
buffer, offset, numbytes,
responder);
@ -130,11 +126,11 @@ LLLFSThread::handle_t LLLFSThread::write(const std::string& filename,
//============================================================================
LLLFSThread::Request::Request(LLLFSThread* thread,
handle_t handle, U32 priority,
handle_t handle,
operation_t op, const std::string& filename,
U8* buffer, S32 offset, S32 numbytes,
Responder* responder) :
QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
QueuedRequest(handle, FLAG_AUTO_COMPLETE),
mThread(thread),
mOperation(op),
mFileName(filename),
@ -157,6 +153,7 @@ LLLFSThread::Request::~Request()
// virtual, called from own thread
void LLLFSThread::Request::finishRequest(bool completed)
{
LL_PROFILE_ZONE_SCOPED;
if (mResponder.notNull())
{
mResponder->completed(completed ? mBytesRead : 0);
@ -166,6 +163,7 @@ void LLLFSThread::Request::finishRequest(bool completed)
void LLLFSThread::Request::deleteRequest()
{
LL_PROFILE_ZONE_SCOPED;
if (getStatus() == STATUS_QUEUED)
{
LL_ERRS() << "Attempt to delete a queued LLLFSThread::Request!" << LL_ENDL;
@ -180,6 +178,7 @@ void LLLFSThread::Request::deleteRequest()
bool LLLFSThread::Request::processRequest()
{
LL_PROFILE_ZONE_SCOPED;
bool complete = false;
if (mOperation == FILE_READ)
{

View File

@ -68,7 +68,7 @@ public:
public:
Request(LLLFSThread* thread,
handle_t handle, U32 priority,
handle_t handle,
operation_t op, const std::string& filename,
U8* buffer, S32 offset, S32 numbytes,
Responder* responder);
@ -120,23 +120,16 @@ public:
// Return a Request handle
handle_t read(const std::string& filename, /* Flawfinder: ignore */
U8* buffer, S32 offset, S32 numbytes,
Responder* responder, U32 pri=0);
Responder* responder);
handle_t write(const std::string& filename,
U8* buffer, S32 offset, S32 numbytes,
Responder* responder, U32 pri=0);
// Misc
U32 priorityCounter() { return mPriorityCounter-- & PRIORITY_LOWBITS; } // Use to order IO operations
Responder* responder);
// static initializers
static void initClass(bool local_is_threaded = TRUE); // Setup sLocal
static S32 updateClass(U32 ms_elapsed);
static void cleanupClass(); // Delete sLocal
private:
U32 mPriorityCounter;
public:
static LLLFSThread* sLocal; // Default local file thread
};

View File

@ -798,7 +798,6 @@ U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 si
// LLImageRaw
//---------------------------------------------------------------------------
S32 LLImageRaw::sGlobalRawMemory = 0;
S32 LLImageRaw::sRawImageCount = 0;
LLImageRaw::LLImageRaw()
@ -815,6 +814,15 @@ LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
++sRawImageCount;
}
LLImageRaw::LLImageRaw(const U8* data, U16 width, U16 height, S8 components)
: LLImageBase()
{
if (allocateDataSize(width, height, components))
{
memcpy(getData(), data, width * height * components);
}
}
LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy)
: LLImageBase()
{
@ -847,16 +855,13 @@ LLImageRaw::~LLImageRaw()
U8* LLImageRaw::allocateData(S32 size)
{
U8* res = LLImageBase::allocateData(size);
sGlobalRawMemory += getDataSize();
return res;
}
// virtual
U8* LLImageRaw::reallocateData(S32 size)
{
sGlobalRawMemory -= getDataSize();
U8* res = LLImageBase::reallocateData(size);
sGlobalRawMemory += getDataSize();
return res;
}
@ -869,7 +874,6 @@ void LLImageRaw::releaseData()
// virtual
void LLImageRaw::deleteData()
{
sGlobalRawMemory -= getDataSize();
LLImageBase::deleteData();
}
@ -985,6 +989,43 @@ void LLImageRaw::verticalFlip()
}
bool LLImageRaw::optimizeAwayAlpha()
{
if (getComponents() == 4)
{
U8* data = getData();
U32 pixels = getWidth() * getHeight();
// check alpha channel for all 255
for (U32 i = 0; i < pixels; ++i)
{
if (data[i * 4 + 3] != 255)
{
return false;
}
}
// alpha channel is all 255, make a new copy of data without alpha channel
U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 3);
for (U32 i = 0; i < pixels; ++i)
{
U32 di = i * 3;
U32 si = i * 4;
for (U32 j = 0; j < 3; ++j)
{
new_data[di+j] = data[si+j];
}
}
setDataAndSize(new_data, getWidth(), getHeight(), 3);
return true;
}
return false;
}
void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
{
// Find new sizes

View File

@ -184,6 +184,7 @@ protected:
public:
LLImageRaw();
LLImageRaw(U16 width, U16 height, S8 components);
LLImageRaw(const U8* data, U16 width, U16 height, S8 components);
LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy = false);
// Construct using createFromFile (used by tools)
//LLImageRaw(const std::string& filename, bool j2c_lowest_mip_only = false);
@ -208,6 +209,10 @@ public:
void verticalFlip();
// if the alpha channel is all 100% opaque, delete it
// returns true if alpha channel was deleted
bool optimizeAwayAlpha();
static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
static S32 contractDimToPowerOfTwo(S32 curr_dim, S32 min_dim = MIN_IMAGE_SIZE);
@ -275,7 +280,6 @@ protected:
void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
public:
static S32 sGlobalRawMemory;
static S32 sRawImageCount;
private:

View File

@ -146,6 +146,7 @@ bool LLImageJ2C::initEncode(LLImageRaw &raw_image, int blocks_size, int precinct
bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
return decodeChannels(raw_imagep, decode_time, 0, 4);
}
@ -153,6 +154,7 @@ bool LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
// Returns true to mean done, whether successful or not.
bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
LLTimer elapsed;
bool res = true;

View File

@ -28,64 +28,93 @@
#include "llimageworker.h"
#include "llimagedxt.h"
#include "threadpool.h"
/*--------------------------------------------------------------------------*/
class ImageRequest
{
public:
ImageRequest(const LLPointer<LLImageFormatted>& image,
S32 discard, BOOL needs_aux,
const LLPointer<LLImageDecodeThread::Responder>& responder);
virtual ~ImageRequest();
/*virtual*/ bool processRequest();
/*virtual*/ void finishRequest(bool completed);
private:
// LLPointers stored in ImageRequest MUST be LLPointer instances rather
// than references: we need to increment the refcount when storing these.
// input
LLPointer<LLImageFormatted> mFormattedImage;
S32 mDiscardLevel;
BOOL mNeedsAux;
// output
LLPointer<LLImageRaw> mDecodedImageRaw;
LLPointer<LLImageRaw> mDecodedImageAux;
BOOL mDecodedRaw;
BOOL mDecodedAux;
LLPointer<LLImageDecodeThread::Responder> mResponder;
};
//----------------------------------------------------------------------------
// MAIN THREAD
LLImageDecodeThread::LLImageDecodeThread(bool threaded)
: LLQueuedThread("imagedecode", threaded)
LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/)
{
mCreationMutex = new LLMutex();
mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8));
mThreadPool->start();
}
//virtual
LLImageDecodeThread::~LLImageDecodeThread()
{
delete mCreationMutex ;
}
{}
// MAIN THREAD
// virtual
size_t LLImageDecodeThread::update(F32 max_time_ms)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
LLMutexLock lock(mCreationMutex);
for (creation_list_t::iterator iter = mCreationList.begin();
iter != mCreationList.end(); ++iter)
{
creation_info& info = *iter;
ImageRequest* req = new ImageRequest(info.handle, info.image,
info.priority, info.discard, info.needs_aux,
info.responder);
bool res = addRequest(req);
if (!res)
{
LL_ERRS() << "request added after LLLFSThread::cleanupClass()" << LL_ENDL;
}
}
mCreationList.clear();
S32 res = LLQueuedThread::update(max_time_ms);
return res;
return getPending();
}
LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux, Responder* responder)
size_t LLImageDecodeThread::getPending()
{
return mThreadPool->getQueue().size();
}
LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(
const LLPointer<LLImageFormatted>& image,
S32 discard,
BOOL needs_aux,
const LLPointer<LLImageDecodeThread::Responder>& responder)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
LLMutexLock lock(mCreationMutex);
handle_t handle = generateHandle();
mCreationList.push_back(creation_info(handle, image, priority, discard, needs_aux, responder));
return handle;
// Instantiate the ImageRequest right in the lambda, why not?
bool posted = mThreadPool->getQueue().post(
[req = ImageRequest(image, discard, needs_aux, responder)]
() mutable
{
auto done = req.processRequest();
req.finishRequest(done);
});
if (! posted)
{
LL_DEBUGS() << "Tried to start decoding on shutdown" << LL_ENDL;
// should this return 0?
}
// It's important to our consumer (LLTextureFetchWorker) that we return a
// nonzero handle. It is NOT important that the nonzero handle be unique:
// nothing is ever done with it except to compare it to zero, or zero it.
return 17;
}
// Used by unit test only
// Returns the size of the mutex guarded list as an indication of sanity
S32 LLImageDecodeThread::tut_size()
void LLImageDecodeThread::shutdown()
{
LLMutexLock lock(mCreationMutex);
S32 res = mCreationList.size();
return res;
mThreadPool->close();
}
LLImageDecodeThread::Responder::~Responder()
@ -94,11 +123,10 @@ LLImageDecodeThread::Responder::~Responder()
//----------------------------------------------------------------------------
LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
LLImageDecodeThread::Responder* responder)
: LLQueuedThread::QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
mFormattedImage(image),
ImageRequest::ImageRequest(const LLPointer<LLImageFormatted>& image,
S32 discard, BOOL needs_aux,
const LLPointer<LLImageDecodeThread::Responder>& responder)
: mFormattedImage(image),
mDiscardLevel(discard),
mNeedsAux(needs_aux),
mDecodedRaw(FALSE),
@ -107,7 +135,7 @@ LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatte
{
}
LLImageDecodeThread::ImageRequest::~ImageRequest()
ImageRequest::~ImageRequest()
{
mDecodedImageRaw = NULL;
mDecodedImageAux = NULL;
@ -118,10 +146,10 @@ LLImageDecodeThread::ImageRequest::~ImageRequest()
// Returns true when done, whether or not decode was successful.
bool LLImageDecodeThread::ImageRequest::processRequest()
bool ImageRequest::processRequest()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
const F32 decode_time_slice = .1f;
const F32 decode_time_slice = 0.f; //disable time slicing
bool done = true;
if (!mDecodedRaw && mFormattedImage.notNull())
{
@ -145,7 +173,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
mFormattedImage->getHeight(),
mFormattedImage->getComponents());
}
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms
done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice);
// some decoders are removing data when task is complete and there were errors
mDecodedRaw = done && mDecodedImageRaw->getData();
}
@ -158,14 +186,14 @@ bool LLImageDecodeThread::ImageRequest::processRequest()
mFormattedImage->getHeight(),
1);
}
done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms
done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4);
mDecodedAux = done && mDecodedImageAux->getData();
}
return done;
}
void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
void ImageRequest::finishRequest(bool completed)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
if (mResponder.notNull())
@ -175,10 +203,3 @@ void LLImageDecodeThread::ImageRequest::finishRequest(bool completed)
}
// Will automatically be deleted
}
// Used by unit test only
// Checks that a responder exists for this instance so that something can happen when completion is reached
bool LLImageDecodeThread::ImageRequest::tut_isOK()
{
return mResponder.notNull();
}

View File

@ -29,9 +29,9 @@
#include "llimage.h"
#include "llpointer.h"
#include "llworkerthread.h"
#include "threadpool_fwd.h"
class LLImageDecodeThread : public LLQueuedThread
class LLImageDecodeThread
{
public:
class Responder : public LLThreadSafeRefCount
@ -42,63 +42,24 @@ public:
virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0;
};
class ImageRequest : public LLQueuedThread::QueuedRequest
{
protected:
virtual ~ImageRequest(); // use deleteRequest()
public:
ImageRequest(handle_t handle, LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
LLImageDecodeThread::Responder* responder);
/*virtual*/ bool processRequest();
/*virtual*/ void finishRequest(bool completed);
// Used by unit tests to check the consitency of the request instance
bool tut_isOK();
private:
// input
LLPointer<LLImageFormatted> mFormattedImage;
S32 mDiscardLevel;
BOOL mNeedsAux;
// output
LLPointer<LLImageRaw> mDecodedImageRaw;
LLPointer<LLImageRaw> mDecodedImageAux;
BOOL mDecodedRaw;
BOOL mDecodedAux;
LLPointer<LLImageDecodeThread::Responder> mResponder;
};
public:
LLImageDecodeThread(bool threaded = true);
virtual ~LLImageDecodeThread();
handle_t decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
Responder* responder);
// meant to resemble LLQueuedThread::handle_t
typedef U32 handle_t;
handle_t decodeImage(const LLPointer<LLImageFormatted>& image,
S32 discard, BOOL needs_aux,
const LLPointer<Responder>& responder);
size_t getPending();
size_t update(F32 max_time_ms);
// Used by unit tests to check the consistency of the thread instance
S32 tut_size();
void shutdown();
private:
struct creation_info
{
handle_t handle;
LLPointer<LLImageFormatted> image;
U32 priority;
S32 discard;
BOOL needs_aux;
LLPointer<Responder> responder;
creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r)
: handle(h), image(i), priority(p), discard(d), needs_aux(aux), responder(r)
{}
};
typedef std::list<creation_info> creation_list_t;
creation_list_t mCreationList;
LLMutex* mCreationMutex;
// As of SL-17483, LLImageDecodeThread is no longer itself an
// LLQueuedThread - instead this is the API by which we submit work to the
// "ImageDecode" ThreadPool.
std::unique_ptr<LL::ThreadPool> mThreadPool;
};
#endif

View File

@ -125,42 +125,11 @@ namespace tut
}
};
// Test wrapper declaration : image worker
// Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance
// but it's not a bad idea to get its public API a good shake as part of a thorough unit test set.
// Some gotcha with the destructor though (see below).
struct imagerequest_test
{
// Instance to be tested
LLImageDecodeThread::ImageRequest* mRequest;
bool done;
// Constructor and destructor of the test wrapper
imagerequest_test()
{
done = false;
mRequest = new LLImageDecodeThread::ImageRequest(0, 0,
LLQueuedThread::PRIORITY_NORMAL, 0, FALSE,
new responder_test(&done));
}
~imagerequest_test()
{
// We should delete the object *but*, because its destructor is protected, that cannot be
// done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine...
//delete mRequest;
}
};
// Tut templating thingamagic: test group, object and test instance
typedef test_group<imagedecodethread_test> imagedecodethread_t;
typedef imagedecodethread_t::object imagedecodethread_object_t;
tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread");
typedef test_group<imagerequest_test> imagerequest_t;
typedef imagerequest_t::object imagerequest_object_t;
tut::imagerequest_t tut_imagerequest("LLImageRequest");
// ---------------------------------------------------------------------------------------
// Test functions
// Notes:
@ -172,64 +141,18 @@ namespace tut
// ---------------------------------------------------------------------------------------
// Test the LLImageDecodeThread interface
// ---------------------------------------------------------------------------------------
//
// Note on Unit Testing Queued Thread Classes
//
// Since methods on such a class are called on a separate loop and that we can't insert tut
// ensure() calls in there, we exercise the class with 2 sets of tests:
// - 1: Test as a single threaded instance: We declare the class but ask for no thread
// to be spawned (easy with LLThreads since there's a boolean argument on the constructor
// just for that). We can then unit test each public method like we do on a normal class.
// - 2: Test as a threaded instance: We let the thread launch and check that its external
// behavior is as expected (i.e. it runs, can accept a work order and processes
// it). Typically though there's no guarantee that this exercises all the methods of the
// class which is why we also need the previous "non threaded" set of unit tests for
// complete coverage.
//
// ---------------------------------------------------------------------------------------
template<> template<>
void imagedecodethread_object_t::test<1>()
{
// Test a *non threaded* instance of the class
mThread = new LLImageDecodeThread(false);
ensure("LLImageDecodeThread: non threaded constructor failed", mThread != NULL);
// Test that we start with an empty list right at creation
ensure("LLImageDecodeThread: non threaded init state incorrect", mThread->tut_size() == 0);
// Insert something in the queue
bool done = false;
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
// Verifies we got a valid handle
ensure("LLImageDecodeThread: non threaded decodeImage(), returned handle is null", decodeHandle != 0);
// Verifies that we do now have something in the queued list
ensure("LLImageDecodeThread: non threaded decodeImage() insertion in threaded list failed", mThread->tut_size() == 1);
// Trigger queue handling "manually" (on a threaded instance, this is done on the thread loop)
S32 res = mThread->update(0);
// Verifies that we successfully handled the list
ensure("LLImageDecodeThread: non threaded update() list handling test failed", res == 0);
// Verifies that the list is now empty
ensure("LLImageDecodeThread: non threaded update() list emptying test failed", mThread->tut_size() == 0);
}
template<> template<>
void imagedecodethread_object_t::test<2>()
{
// Test a *threaded* instance of the class
mThread = new LLImageDecodeThread(true);
ensure("LLImageDecodeThread: threaded constructor failed", mThread != NULL);
// Test that we start with an empty list right at creation
ensure("LLImageDecodeThread: threaded init state incorrect", mThread->tut_size() == 0);
// Insert something in the queue
bool done = false;
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, LLQueuedThread::PRIORITY_NORMAL, 0, FALSE, new responder_test(&done));
LLImageDecodeThread::handle_t decodeHandle = mThread->decodeImage(NULL, 0, FALSE, new responder_test(&done));
// Verifies we get back a valid handle
ensure("LLImageDecodeThread: threaded decodeImage(), returned handle is null", decodeHandle != 0);
// Wait a little so to simulate the main thread doing something on its main loop...
ms_sleep(500); // 500 milliseconds
// Verifies that the responder has *not* been called yet in the meantime
ensure("LLImageDecodeThread: responder creation failed", done == false);
// Ask the thread to update: that means tells the queue to check itself and creates work requests
mThread->update(1);
// Wait till the thread has time to handle the work order (though it doesn't do much per work order...)
const U32 INCREMENT_TIME = 500; // 500 milliseconds
const U32 MAX_TIME = 20 * INCREMENT_TIME; // Do the loop 20 times max, i.e. wait 10 seconds but no more
@ -242,24 +165,4 @@ namespace tut
// Verifies that the responder has now been called
ensure("LLImageDecodeThread: threaded work unit not processed", done == true);
}
// ---------------------------------------------------------------------------------------
// Test the LLImageDecodeThread::ImageRequest interface
// ---------------------------------------------------------------------------------------
template<> template<>
void imagerequest_object_t::test<1>()
{
// Test that we start with a correct request at creation
ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK());
bool res = mRequest->processRequest();
// Verifies that we processed the request successfully
ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true);
// Check that we can call the finishing call safely
try {
mRequest->finishRequest(false);
} catch (...) {
fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed");
}
}
}

View File

@ -726,6 +726,7 @@ bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int disca
bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
// No specific implementation for this method in the OpenJpeg case
return false;
}

View File

@ -122,6 +122,7 @@ LLFolderDictionary::LLFolderDictionary()
addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE, FALSE, FALSE));
addEntry(LLFolderType::FT_SETTINGS, new FolderEntry("settings", TRUE, FALSE, TRUE));
addEntry(LLFolderType::FT_MATERIAL, new FolderEntry("material", TRUE, FALSE, TRUE));
addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE, FALSE, FALSE));
};

View File

@ -93,9 +93,13 @@ public:
FT_SETTINGS = 56,
FT_MATERIAL = 57,
FT_COUNT,
FT_NONE = -1
// When adding, see note at bottom of LLAssetType::Etype
};
static EType lookup(const std::string& type_name);

View File

@ -904,24 +904,31 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
mInventoryType = LLInventoryType::IT_NONE;
mAssetUUID.setNull();
}
std::string w;
w = INV_ITEM_ID_LABEL;
if (sd.has(w))
{
mUUID = sd[w];
}
w = INV_PARENT_ID_LABEL;
if (sd.has(w))
{
mParentUUID = sd[w];
}
// TODO - figure out if this should be moved into the noclobber fields above
mThumbnailUUID.setNull();
w = INV_THUMBNAIL_LABEL;
if (sd.has(w))
// iterate as map to avoid making unnecessary temp copies of everything
LLSD::map_const_iterator i, end;
end = sd.endMap();
for (i = sd.beginMap(); i != end; ++i)
{
const LLSD &thumbnail_map = sd[w];
w = INV_ASSET_ID_LABEL;
if (i->first == INV_ITEM_ID_LABEL)
{
mUUID = i->second;
continue;
}
if (i->first == INV_PARENT_ID_LABEL)
{
mParentUUID = i->second;
continue;
}
if (i->first == INV_THUMBNAIL_LABEL)
{
const LLSD &thumbnail_map = i->second;
const std::string w = INV_ASSET_ID_LABEL;
if (thumbnail_map.has(w))
{
mThumbnailUUID = thumbnail_map[w];
@ -936,22 +943,22 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
<key>version</key>
<integer> 1 </key>
*/
continue;
}
else
if (i->first == INV_THUMBNAIL_ID_LABEL)
{
w = INV_THUMBNAIL_ID_LABEL;
if (sd.has(w))
mThumbnailUUID = i->second.asUUID();
continue;
}
if (i->first == INV_PERMISSIONS_LABEL)
{
mThumbnailUUID = sd[w].asUUID();
mPermissions = ll_permissions_from_sd(i->second);
continue;
}
}
w = INV_PERMISSIONS_LABEL;
if (sd.has(w))
{
mPermissions = ll_permissions_from_sd(sd[w]);
}
w = INV_SALE_INFO_LABEL;
if (sd.has(w))
if (i->first == INV_SALE_INFO_LABEL)
{
// Sale info used to contain next owner perm. It is now in
// the permissions. Thus, we read that out, and fix legacy
@ -959,96 +966,110 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
// should pick up the vast majority of the tasks.
BOOL has_perm_mask = FALSE;
U32 perm_mask = 0;
if (!mSaleInfo.fromLLSD(sd[w], has_perm_mask, perm_mask))
if (!mSaleInfo.fromLLSD(i->second, has_perm_mask, perm_mask))
{
goto fail;
return false;
}
if (has_perm_mask)
{
if(perm_mask == PERM_NONE)
if (perm_mask == PERM_NONE)
{
perm_mask = mPermissions.getMaskOwner();
}
// fair use fix.
if(!(perm_mask & PERM_COPY))
if (!(perm_mask & PERM_COPY))
{
perm_mask |= PERM_TRANSFER;
}
mPermissions.setMaskNext(perm_mask);
}
continue;
}
w = INV_SHADOW_ID_LABEL;
if (sd.has(w))
if (i->first == INV_SHADOW_ID_LABEL)
{
mAssetUUID = sd[w];
mAssetUUID = i->second;
LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
continue;
}
w = INV_ASSET_ID_LABEL;
if (sd.has(w))
if (i->first == INV_ASSET_ID_LABEL)
{
mAssetUUID = sd[w];
mAssetUUID = i->second;
continue;
}
w = INV_LINKED_ID_LABEL;
if (sd.has(w))
if (i->first == INV_LINKED_ID_LABEL)
{
mAssetUUID = sd[w];
mAssetUUID = i->second;
continue;
}
w = INV_ASSET_TYPE_LABEL;
if (sd.has(w))
if (i->first == INV_ASSET_TYPE_LABEL)
{
if (sd[w].isString())
LLSD const &label = i->second;
if (label.isString())
{
mType = LLAssetType::lookup(sd[w].asString().c_str());
mType = LLAssetType::lookup(label.asString().c_str());
}
else if (sd[w].isInteger())
else if (label.isInteger())
{
S8 type = (U8)sd[w].asInteger();
S8 type = (U8) label.asInteger();
mType = static_cast<LLAssetType::EType>(type);
}
continue;
}
w = INV_INVENTORY_TYPE_LABEL;
if (sd.has(w))
if (i->first == INV_INVENTORY_TYPE_LABEL)
{
if (sd[w].isString())
LLSD const &label = i->second;
if (label.isString())
{
mInventoryType = LLInventoryType::lookup(sd[w].asString().c_str());
mInventoryType = LLInventoryType::lookup(label.asString().c_str());
}
else if (sd[w].isInteger())
else if (label.isInteger())
{
S8 type = (U8)sd[w].asInteger();
S8 type = (U8) label.asInteger();
mInventoryType = static_cast<LLInventoryType::EType>(type);
}
continue;
}
w = INV_FLAGS_LABEL;
if (sd.has(w))
if (i->first == INV_FLAGS_LABEL)
{
if (sd[w].isBinary())
LLSD const &label = i->second;
if (label.isBinary())
{
mFlags = ll_U32_from_sd(sd[w]);
mFlags = ll_U32_from_sd(label);
}
else if(sd[w].isInteger())
else if (label.isInteger())
{
mFlags = sd[w].asInteger();
mFlags = label.asInteger();
}
continue;
}
w = INV_NAME_LABEL;
if (sd.has(w))
if (i->first == INV_NAME_LABEL)
{
mName = sd[w].asString();
mName = i->second.asString();
LLStringUtil::replaceNonstandardASCII(mName, ' ');
LLStringUtil::replaceChar(mName, '|', ' ');
continue;
}
w = INV_DESC_LABEL;
if (sd.has(w))
if (i->first == INV_DESC_LABEL)
{
mDescription = sd[w].asString();
mDescription = i->second.asString();
LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
continue;
}
w = INV_CREATION_DATE_LABEL;
if (sd.has(w))
if (i->first == INV_CREATION_DATE_LABEL)
{
mCreationDate = sd[w].asInteger();
mCreationDate = i->second.asInteger();
continue;
}
}
// Need to convert 1.0 simstate files to a useful inventory type
@ -1064,9 +1085,6 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
mPermissions.initMasks(mInventoryType);
return true;
fail:
return false;
}
///----------------------------------------------------------------------------

View File

@ -86,6 +86,7 @@ LLInventoryDictionary::LLInventoryDictionary()
addEntry(LLInventoryType::IT_WIDGET, new InventoryEntry("widget", "widget", 1, LLAssetType::AT_WIDGET));
addEntry(LLInventoryType::IT_PERSON, new InventoryEntry("person", "person", 1, LLAssetType::AT_PERSON));
addEntry(LLInventoryType::IT_SETTINGS, new InventoryEntry("settings", "settings", 1, LLAssetType::AT_SETTINGS));
addEntry(LLInventoryType::IT_MATERIAL, new InventoryEntry("material", "render material", 1, LLAssetType::AT_MATERIAL));
}
@ -153,7 +154,8 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] =
LLInventoryType::IT_NONE, // 53 AT_RESERVED_4
LLInventoryType::IT_NONE, // 54 AT_RESERVED_5
LLInventoryType::IT_SETTINGS, // 55 AT_SETTINGS
LLInventoryType::IT_SETTINGS, // 55 AT_SETTINGS <- why doesnt this match the value in llassettype.h? -brad
LLInventoryType::IT_MATERIAL, // 57 AT_MATERIAL
};
// static

View File

@ -65,7 +65,8 @@ public:
IT_WIDGET = 23,
IT_PERSON = 24,
IT_SETTINGS = 25,
IT_COUNT = 26,
IT_MATERIAL = 26,
IT_COUNT = 27,
IT_UNKNOWN = 255,
IT_NONE = -1
@ -118,6 +119,8 @@ public:
ICONNAME_SETTINGS_WATER,
ICONNAME_SETTINGS_DAY,
ICONNAME_MATERIAL,
ICONNAME_INVALID,
ICONNAME_UNKNOWN,
ICONNAME_COUNT,

View File

@ -694,6 +694,7 @@ void LLSettingsBlender::update(const LLSettingsBase::BlendFactor& blendf)
F64 LLSettingsBlender::setBlendFactor(const LLSettingsBase::BlendFactor& blendf_in)
{
LLSettingsBase::TrackPosition blendf = blendf_in;
llassert(!isnan(blendf));
if (blendf >= 1.0)
{
triggerComplete();

View File

@ -467,6 +467,7 @@ protected:
class LLSettingsBlenderTimeDelta : public LLSettingsBlender
{
protected:
LOG_CLASS(LLSettingsBlenderTimeDelta);
public:
static const LLSettingsBase::BlendFactor MIN_BLEND_DELTA;

View File

@ -33,6 +33,7 @@
#include "v3colorutil.h"
#include <boost/bind.hpp>
//=========================================================================
namespace
{
@ -132,7 +133,11 @@ const std::string LLSettingsSky::SETTING_SKY_MOISTURE_LEVEL("moisture_level");
const std::string LLSettingsSky::SETTING_SKY_DROPLET_RADIUS("droplet_radius");
const std::string LLSettingsSky::SETTING_SKY_ICE_LEVEL("ice_level");
const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("3ae23978-ac82-bcf3-a9cb-ba6e52dcb9ad");
const std::string LLSettingsSky::SETTING_REFLECTION_PROBE_AMBIANCE("reflection_probe_ambiance");
const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("651510b8-5f4d-8991-1592-e7eeab2a5a06");
F32 LLSettingsSky::sAutoAdjustProbeAmbiance = 1.f;
static const LLUUID DEFAULT_SUN_ID("32bfbcea-24b1-fb9d-1ef9-48a28a63730f"); // dataserver
static const LLUUID DEFAULT_MOON_ID("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver
@ -402,6 +407,7 @@ LLSettingsSky::LLSettingsSky(const LLSD &data) :
mNextRainbowTextureId(),
mNextHaloTextureId()
{
mCanAutoAdjust = !data.has(SETTING_REFLECTION_PROBE_AMBIANCE);
}
LLSettingsSky::LLSettingsSky():
@ -424,6 +430,8 @@ void LLSettingsSky::replaceSettings(LLSD settings)
mNextBloomTextureId.setNull();
mNextRainbowTextureId.setNull();
mNextHaloTextureId.setNull();
mCanAutoAdjust = !settings.has(SETTING_REFLECTION_PROBE_AMBIANCE);
}
void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
@ -436,6 +444,7 @@ void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
mNextBloomTextureId = pother->mNextBloomTextureId;
mNextRainbowTextureId = pother->mNextRainbowTextureId;
mNextHaloTextureId = pother->mNextHaloTextureId;
mCanAutoAdjust = pother->mCanAutoAdjust;
}
void LLSettingsSky::blend(const LLSettingsBase::ptr_t &end, F64 blendf)
@ -628,6 +637,9 @@ LLSettingsSky::validation_list_t LLSettingsSky::validationList()
validation.push_back(Validator(SETTING_SKY_ICE_LEVEL, false, LLSD::TypeReal,
boost::bind(&Validator::verifyFloatRange, _1, _2, llsd::array(0.0f, 1.0f))));
validation.push_back(Validator(SETTING_REFLECTION_PROBE_AMBIANCE, false, LLSD::TypeReal,
boost::bind(&Validator::verifyFloatRange, _1, _2, LLSD(llsd::array(0.0f, 10.0f)))));
validation.push_back(Validator(SETTING_RAYLEIGH_CONFIG, true, LLSD::TypeArray, &validateRayleighLayers));
validation.push_back(Validator(SETTING_ABSORPTION_CONFIG, true, LLSD::TypeArray, &validateAbsorptionLayers));
validation.push_back(Validator(SETTING_MIE_CONFIG, true, LLSD::TypeArray, &validateMieLayers));
@ -753,6 +765,8 @@ LLSD LLSettingsSky::defaults(const LLSettingsBase::TrackPosition& position)
dfltsetting[SETTING_SKY_DROPLET_RADIUS] = 800.0f;
dfltsetting[SETTING_SKY_ICE_LEVEL] = 0.0f;
dfltsetting[SETTING_REFLECTION_PROBE_AMBIANCE] = 0.0f;
dfltsetting[SETTING_RAYLEIGH_CONFIG] = rayleighConfigDefault();
dfltsetting[SETTING_MIE_CONFIG] = mieConfigDefault();
dfltsetting[SETTING_ABSORPTION_CONFIG] = absorptionConfigDefault();
@ -1130,6 +1144,12 @@ void LLSettingsSky::setSkyIceLevel(F32 ice_level)
setValue(SETTING_SKY_ICE_LEVEL, ice_level);
}
void LLSettingsSky::setReflectionProbeAmbiance(F32 ambiance)
{
mCanAutoAdjust = false; // we've now touched this sky in a "new" way, it can no longer auto adjust
setValue(SETTING_REFLECTION_PROBE_AMBIANCE, ambiance);
}
void LLSettingsSky::setAmbientColor(const LLColor3 &val)
{
mSettings[SETTING_LEGACY_HAZE][SETTING_AMBIENT] = val.getValue();
@ -1418,6 +1438,34 @@ F32 LLSettingsSky::getSkyIceLevel() const
return mSettings[SETTING_SKY_ICE_LEVEL].asReal();
}
F32 LLSettingsSky::getReflectionProbeAmbiance(bool auto_adjust) const
{
if (auto_adjust && canAutoAdjust())
{
return sAutoAdjustProbeAmbiance;
}
return mSettings[SETTING_REFLECTION_PROBE_AMBIANCE].asReal();
}
F32 LLSettingsSky::getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust) const
{
#if 0
// feed cloud shadow back into reflection probe ambiance to mimic pre-reflection-probe behavior
// without brightening dark/interior spaces
F32 probe_ambiance = getReflectionProbeAmbiance(auto_adjust);
if (probe_ambiance > 0.f && probe_ambiance < 1.f)
{
probe_ambiance += (1.f - probe_ambiance) * getCloudShadow() * cloud_shadow_scale;
}
return probe_ambiance;
#else
return getReflectionProbeAmbiance(auto_adjust);
#endif
}
F32 LLSettingsSky::getSkyBottomRadius() const
{
return mSettings[SETTING_SKY_BOTTOM_RADIUS].asReal();

View File

@ -97,10 +97,14 @@ public:
static const std::string SETTING_SKY_DROPLET_RADIUS;
static const std::string SETTING_SKY_ICE_LEVEL;
static const std::string SETTING_REFLECTION_PROBE_AMBIANCE;
static const std::string SETTING_LEGACY_HAZE;
static const LLUUID DEFAULT_ASSET_ID;
static F32 sAutoAdjustProbeAmbiance;
typedef PTR_NAMESPACE::shared_ptr<LLSettingsSky> ptr_t;
//---------------------------------------------------------------------
@ -131,6 +135,14 @@ public:
F32 getSkyDropletRadius() const;
F32 getSkyIceLevel() const;
// get the probe ambiance setting as stored in the sky settings asset
// auto_adjust - if true and canAutoAdjust() is true, return 1.0
F32 getReflectionProbeAmbiance(bool auto_adjust = false) const;
// get the probe ambiance setting to use for rendering (adjusted by cloud shadow, aka cloud coverage)
// auto_adjust - if true and canAutoAdjust() is true, return 1.0
F32 getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust = false) const;
// Return first (only) profile layer represented in LLSD
LLSD getRayleighConfig() const;
LLSD getMieConfig() const;
@ -159,6 +171,8 @@ public:
void setSkyDropletRadius(F32 radius);
void setSkyIceLevel(F32 ice_level);
void setReflectionProbeAmbiance(F32 ambiance);
//---------------------------------------------------------------------
LLColor3 getAmbientColor() const;
void setAmbientColor(const LLColor3 &val);
@ -324,6 +338,10 @@ public:
F32 aniso_factor = 0.0f);
virtual void updateSettings() SETTINGS_OVERRIDE;
// if true, this sky is a candidate for auto-adjustment
bool canAutoAdjust() const { return mCanAutoAdjust; }
protected:
static const std::string SETTING_LEGACY_EAST_ANGLE;
static const std::string SETTING_LEGACY_ENABLE_CLOUD_SCROLL;
@ -367,6 +385,9 @@ private:
mutable LLColor4 mTotalAmbient;
mutable LLColor4 mHazeColor;
// if true, this sky is a candidate for auto adjustment
bool mCanAutoAdjust = true;
typedef std::map<std::string, S32> mapNameToUniformId_t;
static mapNameToUniformId_t sNameToUniformMapping;

View File

@ -231,7 +231,7 @@ LLSettingsWater::validation_list_t LLSettingsWater::validationList()
llsd::array(0.0f, 0.0f, 0.0f, 1.0f),
llsd::array(1.0f, 1.0f, 1.0f, 1.0f))));
validation.push_back(Validator(SETTING_FOG_DENSITY, true, LLSD::TypeReal,
boost::bind(&Validator::verifyFloatRange, _1, _2, llsd::array(-10.0f, 10.0f))));
boost::bind(&Validator::verifyFloatRange, _1, _2, llsd::array(0.001f, 100.0f))));
validation.push_back(Validator(SETTING_FOG_MOD, true, LLSD::TypeReal,
boost::bind(&Validator::verifyFloatRange, _1, _2, llsd::array(0.0f, 20.0f))));
validation.push_back(Validator(SETTING_FRESNEL_OFFSET, true, LLSD::TypeReal,

View File

@ -379,6 +379,7 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
void LLImageJ2CKDU::cleanupCodeStream()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
mInputp.reset();
mDecodeState.reset();
mCodeStreamp.reset();
@ -426,6 +427,7 @@ bool LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc
// decodeImpl() usage matters for production.
bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level, int* region)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
base.resetLastError();
// *FIX: kdu calls our callback function if there's an error, and then bombs.
@ -509,6 +511,7 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
// Returns true to mean done, whether successful or not.
bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
ECodeStreamMode mode = MODE_FAST;
LLTimer decode_timer;
@ -1332,6 +1335,7 @@ the `buf' pointer may actually point into a larger buffer representing
multiple tiles. For this reason, `row_gap' is needed to identify the
separation between consecutive rows in the real buffer. */
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
S32 c;
// Now walk through the lines of the buffer, recovering them from the
// relevant tile-component processing engines.
@ -1339,17 +1343,26 @@ separation between consecutive rows in the real buffer. */
LLTimer decode_timer;
while (mDims.size.y--)
{
{
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - pull");
for (c = 0; c < mNumComponents; c++)
{
mEngines[c].pull(mLines[c]);
}
}
if ((mNumComponents >= 3) && mUseYCC)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - convert");
kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]);
}
{
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("kduptc - transfer");
for (c = 0; c < mNumComponents; c++)
{
transfer_bytes(mBuf+c,mLines[c],mNumComponents,mBitDepths[c]);
transfer_bytes(mBuf + c, mLines[c], mNumComponents, mBitDepths[c]);
}
}
mBuf += mRowGap;
if (mDims.size.y % 10)

View File

@ -311,104 +311,6 @@ int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 rad
return 0;
}
// HACK: This version is still around because the version below doesn't work
// unless the agent planes are initialized.
// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
// NOTE: 'center' is in absolute frame.
int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const
{
// Returns 1 if sphere is in frustum, 0 if not.
// modified so that default view frust is along X with Z vertical
F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
// Subtract the view position
//LLVector3 relative_center;
//relative_center = sphere_center - getOrigin();
LLVector3 rel_center(sphere_center);
rel_center -= mOrigin;
bool all_in = TRUE;
// Transform relative_center.x to camera frame
x = mXAxis * rel_center;
if (x < MIN_NEAR_PLANE - radius)
{
return 0;
}
else if (x < MIN_NEAR_PLANE + radius)
{
all_in = FALSE;
}
if (x > mFarPlane + radius)
{
return 0;
}
else if (x > mFarPlane - radius)
{
all_in = FALSE;
}
// Transform relative_center.y to camera frame
y = mYAxis * rel_center;
// distance to plane is the dot product of (x, y, 0) * plane_normal
rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
if (rightDist < -radius)
{
return 0;
}
else if (rightDist < radius)
{
all_in = FALSE;
}
leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
if (leftDist < -radius)
{
return 0;
}
else if (leftDist < radius)
{
all_in = FALSE;
}
// Transform relative_center.y to camera frame
z = mZAxis * rel_center;
topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
if (topDist < -radius)
{
return 0;
}
else if (topDist < radius)
{
all_in = FALSE;
}
bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
if (bottomDist < -radius)
{
return 0;
}
else if (bottomDist < radius)
{
all_in = FALSE;
}
if (all_in)
{
return 2;
}
return 1;
}
// HACK: This (presumably faster) version only currently works if you set up the
// frustum planes using GL. At some point we should get those planes through another
// mechanism, and then we can get rid of the "old" version above.
// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
// NOTE: 'center' is in absolute frame.
int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const
@ -463,65 +365,6 @@ F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
}
}
// If pos is visible, return the distance from pos to the camera.
// Use fudge distance to scale rad against top/bot/left/right planes
// Otherwise, return -distance
F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
{
if (mFixedDistance > 0)
{
return mFixedDistance;
}
LLVector3 dvec = pos - mOrigin;
// Check visibility
F32 dist = dvec.magVec();
if (dist > rad)
{
F32 dp,tdist;
dp = dvec * mXAxis;
if (dp < -rad)
return -dist;
rad *= fudgedist;
LLVector3 tvec(pos);
for (int p=0; p<PLANE_NUM; p++)
{
if (!(planemask & (1<<p)))
continue;
tdist = -(mWorldPlanes[p].dist(tvec));
if (tdist > rad)
return -dist;
}
}
return dist;
}
// Like visibleDistance, except uses mHorizPlanes[], which are left and right
// planes perpindicular to (0,0,1) in world space
F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
{
if (mFixedDistance > 0)
{
return mFixedDistance;
}
LLVector3 dvec = pos - mOrigin;
// Check visibility
F32 dist = dvec.magVec();
if (dist > rad)
{
rad *= fudgedist;
LLVector3 tvec(pos);
for (int p=0; p<HORIZ_PLANE_NUM; p++)
{
if (!(planemask & (1<<p)))
continue;
F32 tdist = -(mHorizPlanes[p].dist(tvec));
if (tdist > rad)
return -dist;
}
}
return dist;
}
// ---------------- friends and operators ----------------
@ -536,18 +379,6 @@ std::ostream& operator<<(std::ostream &s, const LLCamera &C)
s << " Aspect = " << C.getAspect() << "\n";
s << " NearPlane = " << C.mNearPlane << "\n";
s << " FarPlane = " << C.mFarPlane << "\n";
s << " TopPlane = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << " "
<< C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << " "
<< C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
s << " BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << " "
<< C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << " "
<< C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
s << " LeftPlane = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << " "
<< C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << " "
<< C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
s << " RightPlane = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << " "
<< C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << " "
<< C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
s << "}";
return s;
}
@ -675,26 +506,6 @@ void LLCamera::calcRegionFrustumPlanes(const LLVector3& shift, F32 far_clip_dist
void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
{
LLVector3 a, b, c;
// For each plane we need to define 3 points (LLVector3's) in camera view space.
// The order in which we pass the points to planeFromPoints() matters, because the
// plane normal has a degeneracy of 2; we want it pointing _into_ the frustum.
a.setVec(0.0f, 0.0f, 0.0f);
b.setVec(mFarPlane, right, top);
c.setVec(mFarPlane, right, bottom);
mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
c.setVec(mFarPlane, left, top);
mLocalPlanes[PLANE_TOP].setVec(a, c, b);
b.setVec(mFarPlane, left, bottom);
mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
c.setVec(mFarPlane, right, bottom);
mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b);
//calculate center and radius squared of frustum in world absolute coordinates
static LLVector3 const X_AXIS(1.f, 0.f, 0.f);
mFrustCenter = X_AXIS*mFarPlane*0.5f;
@ -718,39 +529,6 @@ void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
calculateFrustumPlanes(left, right, top, bottom);
}
void LLCamera::calculateWorldFrustumPlanes()
{
F32 d;
LLVector3 center = mOrigin - mXAxis*mNearPlane;
mWorldPlanePos = center;
LLVector3 pnorm;
for (int p = 0; p < PLANE_NUM; p++)
{
mLocalPlanes[p].getVector3(pnorm);
LLVector3 norm = rotateToAbsolute(pnorm);
norm.normVec();
d = -(center * norm);
mWorldPlanes[p] = LLPlane(norm, d);
}
// horizontal planes, perpindicular to (0,0,1);
LLVector3 zaxis(0, 0, 1.0f);
F32 yaw = getYaw();
{
LLVector3 tnorm;
mLocalPlanes[PLANE_LEFT].getVector3(tnorm);
tnorm.rotVec(yaw, zaxis);
d = -(mOrigin * tnorm);
mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
}
{
LLVector3 tnorm;
mLocalPlanes[PLANE_RIGHT].getVector3(tnorm);
tnorm.rotVec(yaw, zaxis);
d = -(mOrigin * tnorm);
mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
}
}
// NOTE: this is the OpenGL matrix that will transform the default OpenGL view
// (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up):
//

View File

@ -39,7 +39,7 @@ const F32 DEFAULT_NEAR_PLANE = 0.25f;
const F32 DEFAULT_FAR_PLANE = 64.f; // far reaches across two horizontal, not diagonal, regions
const F32 MAX_ASPECT_RATIO = 50.0f;
const F32 MAX_NEAR_PLANE = 10.f;
const F32 MAX_NEAR_PLANE = 1023.9f; // Clamp the near plane just before the skybox ends
const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though.
const F32 MAX_FAR_CLIP = 512.0f;
@ -131,14 +131,10 @@ private:
S32 mViewHeightInPixels; // for ViewHeightInPixels() only
F32 mNearPlane;
F32 mFarPlane;
LL_ALIGN_16(LLPlane mLocalPlanes[PLANE_NUM]);
F32 mFixedDistance; // Always return this distance, unless < 0
LLVector3 mFrustCenter; // center of frustum and radius squared for ultra-quick exclusion test
F32 mFrustRadiusSquared;
LL_ALIGN_16(LLPlane mWorldPlanes[PLANE_NUM]);
LL_ALIGN_16(LLPlane mHorizPlanes[HORIZ_PLANE_NUM]);
U32 mPlaneCount; //defaults to 6, if setUserClipPlane is called, uses user supplied clip plane in
LLVector3 mWorldPlanePos; // Position of World Planes (may be offset from camera)
@ -184,7 +180,6 @@ public:
return atan2f(mXAxis[VZ], xylen);
}
const LLPlane& getWorldPlane(S32 index) const { return mWorldPlanes[index]; }
const LLVector3& getWorldPlanePos() const { return mWorldPlanePos; }
// Copy mView, mAspect, mNearPlane, and mFarPlane to buffer.
@ -200,7 +195,6 @@ public:
// Returns 1 if partly in, 2 if fully in.
// NOTE: 'center' is in absolute frame.
S32 sphereInFrustumOld(const LLVector3 &center, const F32 radius) const;
S32 sphereInFrustum(const LLVector3 &center, const F32 radius) const;
S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); }
S32 sphereInFrustumFull(const LLVector3 &center, const F32 radius) const { return sphereInFrustum(center, radius); }
@ -217,8 +211,6 @@ public:
F32 heightInPixels(const LLVector3 &center, F32 radius ) const;
// return the distance from pos to camera if visible (-distance if not visible)
F32 visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = PLANE_ALL_MASK) const;
F32 visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = HORIZ_PLANE_ALL_MASK) const;
void setFixedDistance(F32 distance) { mFixedDistance = distance; }
friend std::ostream& operator<<(std::ostream &s, const LLCamera &C);
@ -227,7 +219,6 @@ protected:
void calculateFrustumPlanes();
void calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom);
void calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2);
void calculateWorldFrustumPlanes();
} LL_ALIGN_POSTFIX(16);

View File

@ -32,6 +32,7 @@
#include <stdint.h>
#endif
#include <cmath>
#include <unordered_map>
#include "llerror.h"
@ -52,6 +53,11 @@
#include "llmeshoptimizer.h"
#include "lltimer.h"
#include "mikktspace/mikktspace.h"
#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file
#include "meshoptimizer/meshoptimizer.h"
#define DEBUG_SILHOUETTE_BINORMALS 0
#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
@ -2050,7 +2056,8 @@ LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL ge
mDetail = detail;
mSculptLevel = -2;
mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims
mIsMeshAssetLoaded = FALSE;
mIsMeshAssetLoaded = false;
mIsMeshAssetUnavaliable = false;
mLODScaleBias.setVec(1,1,1);
mHullPoints = NULL;
mHullIndices = NULL;
@ -2093,6 +2100,8 @@ void LLVolume::regen()
void LLVolume::genTangents(S32 face)
{
// generate legacy tangents for the specified face
llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents
mVolumeFaces[face].createTangents();
}
@ -2433,11 +2442,10 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
LLSD::Binary pos = mdl[i]["Position"];
LLSD::Binary norm = mdl[i]["Normal"];
LLSD::Binary tangent = mdl[i]["Tangent"];
LLSD::Binary tc = mdl[i]["TexCoord0"];
LLSD::Binary idx = mdl[i]["TriangleList"];
//copy out indices
S32 num_indices = idx.size() / 2;
const S32 indices_to_discard = num_indices % 3;
@ -2492,6 +2500,16 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
//unpack normalized scale/translation
if (mdl[i].has("NormalizedScale"))
{
face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]);
}
else
{
face.mNormalizedScale.set(1, 1, 1);
}
LLVector4a pos_range;
pos_range.setSub(max_pos, min_pos);
LLVector2 tc_range2 = max_tc - min_tc;
@ -2542,6 +2560,34 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
}
}
#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents
{
if (!tangent.empty())
{
face.allocateTangents(face.mNumVertices);
U16* t = (U16*)&(tangent[0]);
// NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to
// maintain compliance with the GLTF spec
LLVector4a* t_out = face.mTangents;
for (U32 j = 0; j < num_verts; ++j)
{
t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]);
t_out->div(65535.f);
t_out->mul(2.f);
t_out->sub(1.f);
F32* tp = t_out->getF32ptr();
tp[3] = tp[3] < 0.f ? -1.f : 1.f;
t_out++;
t += 4;
}
}
}
#endif
{
if (!tc.empty())
{
@ -2745,7 +2791,7 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
}
}
if (!cacheOptimize())
if (!cacheOptimize(true))
{
// Out of memory?
LL_WARNS() << "Failed to optimize!" << LL_ENDL;
@ -2759,14 +2805,32 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
}
BOOL LLVolume::isMeshAssetLoaded()
bool LLVolume::isMeshAssetLoaded()
{
return mIsMeshAssetLoaded;
}
void LLVolume::setMeshAssetLoaded(BOOL loaded)
void LLVolume::setMeshAssetLoaded(bool loaded)
{
mIsMeshAssetLoaded = loaded;
if (loaded)
{
mIsMeshAssetUnavaliable = false;
}
}
void LLVolume::setMeshAssetUnavaliable(bool unavaliable)
{
// Don't set it if at least one lod loaded
if (!mIsMeshAssetLoaded)
{
mIsMeshAssetUnavaliable = unavaliable;
}
}
bool LLVolume::isMeshAssetUnavaliable()
{
return mIsMeshAssetUnavaliable;
}
void LLVolume::copyFacesTo(std::vector<LLVolumeFace> &faces) const
@ -2786,11 +2850,11 @@ void LLVolume::copyVolumeFaces(const LLVolume* volume)
mSculptLevel = 0;
}
bool LLVolume::cacheOptimize()
bool LLVolume::cacheOptimize(bool gen_tangents)
{
for (S32 i = 0; i < mVolumeFaces.size(); ++i)
{
if (!mVolumeFaces[i].cacheOptimize())
if (!mVolumeFaces[i].cacheOptimize(gen_tangents))
{
return false;
}
@ -3306,12 +3370,12 @@ BOOL LLVolume::isFlat(S32 face)
bool LLVolumeParams::isSculpt() const
{
return mSculptID.notNull();
return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE;
}
bool LLVolumeParams::isMeshSculpt() const
{
return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH);
return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH;
}
bool LLVolumeParams::operator==(const LLVolumeParams &params) const
@ -3726,6 +3790,7 @@ bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 h
void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts)
{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the
//supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
F32 detail[] = {1.f, 1.5f, 2.5f, 4.f};
for (S32 i = 0; i < 4; i++)
{
@ -4861,6 +4926,7 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
}
mOptimized = src.mOptimized;
mNormalizedScale = src.mNormalizedScale;
//delete
return *this;
@ -5383,256 +5449,218 @@ public:
}
};
// data structures for tangent generation
bool LLVolumeFace::cacheOptimize()
{ //optimize for vertex cache according to Forsyth method:
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
struct MikktData
{
LLVolumeFace* face;
std::vector<LLVector3> p;
std::vector<LLVector3> n;
std::vector<LLVector2> tc;
std::vector<LLVector4> w;
std::vector<LLVector4> t;
llassert(!mOptimized);
mOptimized = TRUE;
MikktData(LLVolumeFace* f)
: face(f)
{
U32 count = face->mNumIndices;
LLVCacheLRU cache;
p.resize(count);
n.resize(count);
tc.resize(count);
t.resize(count);
if (mNumVertices < 3 || mNumIndices < 3)
{ //nothing to do
return true;
if (face->mWeights)
{
w.resize(count);
}
//mapping of vertices to triangles and indices
std::vector<LLVCacheVertexData> vertex_data;
//mapping of triangles do vertices
std::vector<LLVCacheTriangleData> triangle_data;
LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]);
try
for (int i = 0; i < face->mNumIndices; ++i)
{
triangle_data.resize(mNumIndices / 3);
vertex_data.resize(mNumVertices);
U32 idx = face->mIndices[i];
for (U32 i = 0; i < mNumIndices; i++)
{ //populate vertex data and triangle data arrays
U16 idx = mIndices[i];
U32 tri_idx = i / 3;
p[i].set(face->mPositions[idx].getF32ptr());
p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents
n[i].set(face->mNormals[idx].getF32ptr());
n[i].scaleVec(inv_scale);
n[i].normalize();
tc[i].set(face->mTexCoords[idx]);
if (idx >= mNumVertices)
if (idx >= face->mNumVertices)
{
// invalid index
// replace with a valid index to avoid crashes
idx = mNumVertices - 1;
mIndices[i] = idx;
idx = face->mNumVertices - 1;
face->mIndices[i] = idx;
// Needs better logging
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL;
}
vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx]));
vertex_data[idx].mIdx = idx;
triangle_data[tri_idx].mVertex[i % 3] = &(vertex_data[idx]);
}
}
catch (std::bad_alloc&)
if (face->mWeights)
{
// resize or push_back failed
LL_WARNS("LLVOLUME") << "Resize for " << mNumVertices << " vertices failed" << LL_ENDL;
return false;
}
/*F32 pre_acmr = 1.f;
//measure cache misses from before rebuild
{
LLVCacheFIFO test_cache;
for (U32 i = 0; i < mNumIndices; ++i)
{
test_cache.addVertex(&vertex_data[mIndices[i]]);
}
for (U32 i = 0; i < mNumVertices; i++)
{
vertex_data[i].mCacheTag = -1;
}
pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
}*/
for (U32 i = 0; i < mNumVertices; i++)
{ //initialize score values (no cache -- might try a fifo cache here)
LLVCacheVertexData& data = vertex_data[i];
data.mScore = find_vertex_score(data);
data.mActiveTriangles = data.mTriangles.size();
for (U32 j = 0; j < data.mActiveTriangles; ++j)
{
data.mTriangles[j]->mScore += data.mScore;
}
}
//sort triangle data by score
std::sort(triangle_data.begin(), triangle_data.end());
std::vector<U16> new_indices;
LLVCacheTriangleData* tri;
//prime pump by adding first triangle to cache;
tri = &(triangle_data[0]);
cache.addTriangle(tri);
new_indices.push_back(tri->mVertex[0]->mIdx);
new_indices.push_back(tri->mVertex[1]->mIdx);
new_indices.push_back(tri->mVertex[2]->mIdx);
tri->complete();
//U32 breaks = 0;
for (U32 i = 1; i < mNumIndices/3; ++i)
{
cache.updateScores();
tri = cache.mBestTriangle;
if (!tri)
{
//breaks++;
for (U32 j = 0; j < triangle_data.size(); ++j)
{
if (triangle_data[j].mActive)
{
tri = &(triangle_data[j]);
break;
w[i].set(face->mWeights[idx].getF32ptr());
}
}
}
};
cache.addTriangle(tri);
new_indices.push_back(tri->mVertex[0]->mIdx);
new_indices.push_back(tri->mVertex[1]->mIdx);
new_indices.push_back(tri->mVertex[2]->mIdx);
tri->complete();
bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{ //optimize for vertex cache according to Forsyth method:
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
llassert(!mOptimized);
mOptimized = TRUE;
if (gen_tangents && mNormals && mTexCoords)
{ // generate mikkt space tangents before cache optimizing since the index buffer may change
// a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh
// and is executed on a background thread
SMikkTSpaceInterface ms;
ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
{
MikktData* data = (MikktData*)pContext->m_pUserData;
LLVolumeFace* face = data->face;
return face->mNumIndices / 3;
};
ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace)
{
return 3;
};
ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
{
MikktData* data = (MikktData*)pContext->m_pUserData;
F32* v = data->p[iFace * 3 + iVert].mV;
fvPosOut[0] = v[0];
fvPosOut[1] = v[1];
fvPosOut[2] = v[2];
};
ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
{
MikktData* data = (MikktData*)pContext->m_pUserData;
F32* n = data->n[iFace * 3 + iVert].mV;
fvNormOut[0] = n[0];
fvNormOut[1] = n[1];
fvNormOut[2] = n[2];
};
ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
{
MikktData* data = (MikktData*)pContext->m_pUserData;
F32* tc = data->tc[iFace * 3 + iVert].mV;
fvTexcOut[0] = tc[0];
fvTexcOut[1] = tc[1];
};
ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
{
MikktData* data = (MikktData*)pContext->m_pUserData;
S32 i = iFace * 3 + iVert;
data->t[i].set(fvTangent);
data->t[i].mV[3] = fSign;
};
ms.m_setTSpace = nullptr;
MikktData data(this);
SMikkTSpaceContext ctx = { &ms, &data };
genTangSpaceDefault(&ctx);
//re-weld
meshopt_Stream mos[] =
{
{ &data.p[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &data.n[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &data.t[0], sizeof(LLVector4), sizeof(LLVector4) },
{ &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) },
{ data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) }
};
std::vector<U32> remap;
remap.resize(data.p.size());
U32 stream_count = data.w.empty() ? 4 : 5;
U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
if (vert_count < 65535)
{
std::vector<U32> indices;
indices.resize(mNumIndices);
//copy results back into volume
resizeVertices(vert_count);
if (!data.w.empty())
{
allocateWeights(vert_count);
}
for (U32 i = 0; i < mNumIndices; ++i)
allocateTangents(mNumVertices);
for (int i = 0; i < mNumIndices; ++i)
{
mIndices[i] = new_indices[i];
}
U32 src_idx = i;
U32 dst_idx = remap[i];
mIndices[i] = dst_idx;
/*F32 post_acmr = 1.f;
//measure cache misses from after rebuild
{
LLVCacheFIFO test_cache;
for (U32 i = 0; i < mNumVertices; i++)
{
vertex_data[i].mCacheTag = -1;
}
mPositions[dst_idx].load3(data.p[src_idx].mV);
mNormals[dst_idx].load3(data.n[src_idx].mV);
mTexCoords[dst_idx] = data.tc[src_idx];
for (U32 i = 0; i < mNumIndices; ++i)
{
test_cache.addVertex(&vertex_data[mIndices[i]]);
}
mTangents[dst_idx].loadua(data.t[src_idx].mV);
post_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
}*/
//optimize for pre-TnL cache
//allocate space for new buffer
S32 num_verts = mNumVertices;
S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
LLVector4a* pos = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+size);
if (pos == NULL)
{
LL_WARNS("LLVOLUME") << "Allocation of positions vector[" << sizeof(LLVector4a) * 2 * num_verts + size << "] failed. " << LL_ENDL;
return false;
}
LLVector4a* norm = pos + num_verts;
LLVector2* tc = (LLVector2*) (norm + num_verts);
LLVector4a* wght = NULL;
if (mWeights)
{
wght = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
if (wght == NULL)
mWeights[dst_idx].loadua(data.w[src_idx].mV);
}
}
// put back in normalized coordinate frame
LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]);
LLVector4a scale;
scale.load3(mNormalizedScale.mV);
scale.getF32ptr()[3] = 1.f;
for (int i = 0; i < mNumVertices; ++i)
{
ll_aligned_free<64>(pos);
LL_WARNS("LLVOLUME") << "Allocation of weights[" << sizeof(LLVector4a) * num_verts << "] failed" << LL_ENDL;
return false;
mPositions[i].mul(inv_scale);
mNormals[i].mul(scale);
mNormals[i].normalize3();
F32 w = mTangents[i].getF32ptr()[3];
mTangents[i].mul(scale);
mTangents[i].normalize3();
mTangents[i].getF32ptr()[3] = w;
}
}
LLVector4a* binorm = NULL;
if (mTangents)
else
{
binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
if (binorm == NULL)
{
ll_aligned_free<64>(pos);
ll_aligned_free_16(wght);
LL_WARNS("LLVOLUME") << "Allocation of binormals[" << sizeof(LLVector4a)*num_verts << "] failed" << LL_ENDL;
return false;
// blew past the max vertex size limit, use legacy tangent generation which never adds verts
createTangents();
}
}
//allocate mapping of old indices to new indices
std::vector<S32> new_idx;
// cache optimize index buffer
try
{
new_idx.resize(mNumVertices, -1);
}
catch (std::bad_alloc&)
{
ll_aligned_free<64>(pos);
ll_aligned_free_16(wght);
ll_aligned_free_16(binorm);
LL_WARNS("LLVOLUME") << "Resize failed: " << mNumVertices << LL_ENDL;
return false;
}
// meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy
U16* src_indices = mIndices;
mIndices = nullptr;
resizeIndices(mNumIndices);
S32 cur_idx = 0;
for (U32 i = 0; i < mNumIndices; ++i)
{
U16 idx = mIndices[i];
if (new_idx[idx] == -1)
{ //this vertex hasn't been added yet
new_idx[idx] = cur_idx;
meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices);
//copy vertex data
pos[cur_idx] = mPositions[idx];
norm[cur_idx] = mNormals[idx];
tc[cur_idx] = mTexCoords[idx];
if (mWeights)
{
wght[cur_idx] = mWeights[idx];
}
if (mTangents)
{
binorm[cur_idx] = mTangents[idx];
}
cur_idx++;
}
}
for (U32 i = 0; i < mNumIndices; ++i)
{
mIndices[i] = new_idx[mIndices[i]];
}
ll_aligned_free<64>(mPositions);
// DO NOT free mNormals and mTexCoords as they are part of mPositions buffer
ll_aligned_free_16(mWeights);
ll_aligned_free_16(mTangents);
#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
ll_aligned_free_16(mJointIndices);
ll_aligned_free_16(mJustWeights);
mJustWeights = NULL;
mJointIndices = NULL; // filled in later as necessary by skinning code for acceleration
#endif
mPositions = pos;
mNormals = norm;
mTexCoords = tc;
mWeights = wght;
mTangents = binorm;
//std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
//LL_INFOS() << result << LL_ENDL;
ll_aligned_free_16(src_indices);
return true;
}
@ -6442,35 +6470,31 @@ void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVe
void LLVolumeFace::createTangents()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
if (!mTangents)
{
allocateTangents(mNumVertices);
//generate tangents
//LLVector4a* pos = mPositions;
//LLVector2* tc = (LLVector2*) mTexCoords;
LLVector4a* binorm = (LLVector4a*) mTangents;
LLVector4a* ptr = (LLVector4a*)mTangents;
LLVector4a* end = mTangents+mNumVertices;
while (binorm < end)
LLVector4a* end = mTangents + mNumVertices;
while (ptr < end)
{
(*binorm++).clear();
(*ptr++).clear();
}
binorm = mTangents;
CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices/3, mIndices, mTangents);
//normalize tangents
//normalize normals
for (U32 i = 0; i < mNumVertices; i++)
{
//binorm[i].normalize3fast();
//bump map/planar projection code requires normals to be normalized
mNormals[i].normalize3fast();
}
}
}
void LLVolumeFace::resizeVertices(S32 num_verts)

View File

@ -908,7 +908,7 @@ public:
void remap();
void optimize(F32 angle_cutoff = 2.f);
bool cacheOptimize();
bool cacheOptimize(bool gen_tangents = false);
void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
void destroyOctree();
@ -960,10 +960,6 @@ public:
// indexes for mPositions/mNormals/mTexCoords
U16* mIndices;
// vertex buffer filled in by LLFace to cache this volume face geometry in vram
// (declared as a LLPointer to LLRefCount to avoid dependency on LLVertexBuffer)
mutable LLPointer<LLRefCount> mVertexBuffer;
std::vector<S32> mEdge;
//list of skin weights for rigged volumes
@ -985,6 +981,11 @@ public:
//whether or not face has been cache optimized
BOOL mOptimized;
// if this is a mesh asset, scale and translation that were applied
// when encoding the source mesh into a unit cube
// used for regenerating tangents
LLVector3 mNormalizedScale = LLVector3(1,1,1);
private:
LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
LLVolumeTriangle* mOctreeTriangles;
@ -1087,7 +1088,10 @@ public:
void copyVolumeFaces(const LLVolume* volume);
void copyFacesTo(std::vector<LLVolumeFace> &faces) const;
void copyFacesFrom(const std::vector<LLVolumeFace> &faces);
bool cacheOptimize();
// use meshoptimizer to optimize index buffer for vertex shader cache
// gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer
bool cacheOptimize(bool gen_tangents = false);
private:
void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type);
@ -1107,15 +1111,18 @@ private:
bool unpackVolumeFacesInternal(const LLSD& mdl);
public:
virtual void setMeshAssetLoaded(BOOL loaded);
virtual BOOL isMeshAssetLoaded();
virtual void setMeshAssetLoaded(bool loaded);
virtual bool isMeshAssetLoaded();
virtual void setMeshAssetUnavaliable(bool unavaliable);
virtual bool isMeshAssetUnavaliable();
protected:
BOOL mUnique;
F32 mDetail;
S32 mSculptLevel;
F32 mSurfaceArea; //unscaled surface area
BOOL mIsMeshAssetLoaded;
bool mIsMeshAssetLoaded;
bool mIsMeshAssetUnavaliable;
const LLVolumeParams mParams;
LLPath *mPathp;

View File

@ -89,7 +89,7 @@ BOOL LLVolumeMgr::cleanup()
// Note however that LLVolumeLODGroup that contains the volume
// also holds a LLPointer so the volume will only go away after
// anything holding the volume and the LODGroup are destroyed
LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 detail)
LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod)
{
LLVolumeLODGroup* volgroupp;
if (mDataMutex)
@ -109,7 +109,7 @@ LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32
{
mDataMutex->unlock();
}
return volgroupp->refLOD(detail);
return volgroupp->refLOD(lod);
}
// virtual
@ -287,18 +287,18 @@ bool LLVolumeLODGroup::cleanupRefs()
return res;
}
LLVolume* LLVolumeLODGroup::refLOD(const S32 detail)
LLVolume* LLVolumeLODGroup::refLOD(const S32 lod)
{
llassert(detail >=0 && detail < NUM_LODS);
mAccessCount[detail]++;
llassert(lod >=0 && lod < NUM_LODS);
mAccessCount[lod]++;
mRefs++;
if (mVolumeLODs[detail].isNull())
if (mVolumeLODs[lod].isNull())
{
mVolumeLODs[detail] = new LLVolume(mVolumeParams, mDetailScales[detail]);
mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]);
}
mLODRefs[detail]++;
return mVolumeLODs[detail];
mLODRefs[lod]++;
return mVolumeLODs[lod];
}
BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep)

View File

@ -33,6 +33,7 @@ class LLVector4;
#include "llerror.h"
#include "llmath.h"
#include "llsd.h"
#include "v3math.h" // needed for linearColor3v implemtation below
#include <string.h>
// LLColor3 = |r g b|
@ -88,6 +89,16 @@ public:
const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec
const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec
// set from a vector of unknown type and size
// may leave some data unmodified
template<typename T>
const LLColor3& set(const std::vector<T>& v);
// write to a vector of unknown type and size
// maye leave some data unmodified
template<typename T>
void write(std::vector<T>& v) const;
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
F32 normVec(); // deprecated
@ -484,13 +495,45 @@ inline const LLColor3 srgbColor3(const LLColor3 &a) {
return srgbColor;
}
inline const LLColor3 linearColor3(const LLColor3 &a) {
inline const LLColor3 linearColor3p(const F32* v) {
LLColor3 linearColor;
linearColor.mV[0] = sRGBtoLinear(a.mV[0]);
linearColor.mV[1] = sRGBtoLinear(a.mV[1]);
linearColor.mV[2] = sRGBtoLinear(a.mV[2]);
linearColor.mV[0] = sRGBtoLinear(v[0]);
linearColor.mV[1] = sRGBtoLinear(v[1]);
linearColor.mV[2] = sRGBtoLinear(v[2]);
return linearColor;
}
template<class T>
inline const LLColor3 linearColor3(const T& a) {
return linearColor3p(a.mV);
}
template<class T>
inline const LLVector3 linearColor3v(const T& a) {
return LLVector3(linearColor3p(a.mV).mV);
}
template<typename T>
const LLColor3& LLColor3::set(const std::vector<T>& v)
{
for (S32 i = 0; i < llmin((S32)v.size(), 3); ++i)
{
mV[i] = v[i];
}
return *this;
}
// write to a vector of unknown type and size
// maye leave some data unmodified
template<typename T>
void LLColor3::write(std::vector<T>& v) const
{
for (int i = 0; i < llmin((S32)v.size(), 3); ++i)
{
v[i] = mV[i];
}
}
#endif

View File

@ -88,8 +88,18 @@ class LLColor4
const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha)
const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified
const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec
const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec
const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
// set from a vector of unknown type and size
// may leave some data unmodified
template<typename T>
const LLColor4& set(const std::vector<T>& v);
// write to a vector of unknown type and size
// maye leave some data unmodified
template<typename T>
void write(std::vector<T>& v) const;
const LLColor4& setAlpha(F32 a);
@ -334,6 +344,15 @@ inline const LLColor4& LLColor4::set(const F32 *vec)
return (*this);
}
inline const LLColor4& LLColor4::set(const F64 *vec)
{
mV[VX] = static_cast<F32>(vec[VX]);
mV[VY] = static_cast<F32>(vec[VY]);
mV[VZ] = static_cast<F32>(vec[VZ]);
mV[VW] = static_cast<F32>(vec[VW]);
return (*this);
}
// deprecated
inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z)
{
@ -680,5 +699,25 @@ inline const LLColor4 linearColor4(const LLColor4 &a)
return linearColor;
}
template<typename T>
const LLColor4& LLColor4::set(const std::vector<T>& v)
{
for (S32 i = 0; i < llmin((S32)v.size(), 4); ++i)
{
mV[i] = v[i];
}
return *this;
}
template<typename T>
void LLColor4::write(std::vector<T>& v) const
{
for (int i = 0; i < llmin((S32)v.size(), 4); ++i)
{
v[i] = mV[i];
}
}
#endif

View File

@ -30,6 +30,7 @@ set(llmessage_SOURCE_FILES
lldispatcher.cpp
llexperiencecache.cpp
llfiltersd2xmlrpc.cpp
llgenericstreamingmessage.cpp
llhost.cpp
llhttpnode.cpp
llhttpsdhandler.cpp
@ -114,6 +115,7 @@ set(llmessage_HEADER_FILES
llextendedstatus.h
llfiltersd2xmlrpc.h
llfollowcamparams.h
llgenericstreamingmessage.h
llhost.h
llhttpnode.h
llhttpnodeadapter.h

View File

@ -64,7 +64,6 @@ LLCore::HttpRequest::ptr_t sHttpRequest;
LLCore::HttpHeaders::ptr_t sHttpHeaders;
LLCore::HttpOptions::ptr_t sHttpOptions;
LLCore::HttpRequest::policy_t sHttpPolicy;
LLCore::HttpRequest::priority_t sHttpPriority;
/* Sample response:
<?xml version="1.0"?>
@ -121,7 +120,6 @@ LLAvatarNameCache::LLAvatarNameCache()
sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders());
sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID;
sHttpPriority = 0;
}
LLAvatarNameCache::~LLAvatarNameCache()

View File

@ -131,7 +131,6 @@ bool responseToLLSD(HttpResponse * response, bool log, LLSD & out_llsd)
HttpHandle requestPostWithLLSD(HttpRequest * request,
HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const HttpOptions::ptr_t &options,
@ -145,7 +144,6 @@ HttpHandle requestPostWithLLSD(HttpRequest * request,
LLSDSerialize::toXML(body, bas);
handle = request->requestPost(policy_id,
priority,
url,
ba,
options,
@ -158,7 +156,6 @@ HttpHandle requestPostWithLLSD(HttpRequest * request,
HttpHandle requestPutWithLLSD(HttpRequest * request,
HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const HttpOptions::ptr_t &options,
@ -172,7 +169,6 @@ HttpHandle requestPutWithLLSD(HttpRequest * request,
LLSDSerialize::toXML(body, bas);
handle = request->requestPut(policy_id,
priority,
url,
ba,
options,
@ -184,7 +180,6 @@ HttpHandle requestPutWithLLSD(HttpRequest * request,
HttpHandle requestPatchWithLLSD(HttpRequest * request,
HttpRequest::policy_t policy_id,
HttpRequest::priority_t priority,
const std::string & url,
const LLSD & body,
const HttpOptions::ptr_t &options,
@ -198,7 +193,6 @@ HttpHandle requestPatchWithLLSD(HttpRequest * request,
LLSDSerialize::toXML(body, bas);
handle = request->requestPatch(policy_id,
priority,
url,
ba,
options,
@ -672,10 +666,9 @@ const std::string HttpCoroutineAdapter::HTTP_RESULTS_CONTENT("content");
const std::string HttpCoroutineAdapter::HTTP_RESULTS_RAW("raw");
HttpCoroutineAdapter::HttpCoroutineAdapter(const std::string &name,
LLCore::HttpRequest::policy_t policyId, LLCore::HttpRequest::priority_t priority) :
LLCore::HttpRequest::policy_t policyId) :
mAdapterName(name),
mPolicyId(policyId),
mPriority(priority),
mYieldingHandle(LLCORE_HTTP_HANDLE_INVALID),
mWeakRequest(),
mWeakHandler()
@ -709,7 +702,7 @@ LLSD HttpCoroutineAdapter::postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = requestPostWithLLSD(request,
mPolicyId, mPriority, url, body, options, headers,
mPolicyId, url, body, options, headers,
handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -832,7 +825,7 @@ LLSD HttpCoroutineAdapter::postAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = request->requestPost(mPolicyId, mPriority, url, rawbody.get(),
LLCore::HttpHandle hhandle = request->requestPost(mPolicyId, url, rawbody.get(),
options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -890,7 +883,7 @@ LLSD HttpCoroutineAdapter::putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = requestPutWithLLSD(request,
mPolicyId, mPriority, url, body, options, headers,
mPolicyId, url, body, options, headers,
handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -916,7 +909,7 @@ LLSD HttpCoroutineAdapter::putAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = request->requestPut(mPolicyId, mPriority,
LLCore::HttpHandle hhandle = request->requestPut(mPolicyId,
url, rawbody.get(), options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -972,7 +965,7 @@ LLSD HttpCoroutineAdapter::getAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = request->requestGet(mPolicyId, mPriority,
LLCore::HttpHandle hhandle = request->requestGet(mPolicyId,
url, options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -1018,7 +1011,7 @@ LLSD HttpCoroutineAdapter::deleteAndSuspend_(LLCore::HttpRequest::ptr_t &request
checkDefaultHeaders(headers);
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = request->requestDelete(mPolicyId, mPriority,
LLCore::HttpHandle hhandle = request->requestDelete(mPolicyId,
url, options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -1056,7 +1049,7 @@ LLSD HttpCoroutineAdapter::patchAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
LLCore::HttpHandle hhandle = requestPatchWithLLSD(request,
mPolicyId, mPriority, url, body, options, headers,
mPolicyId, url, body, options, headers,
handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -1098,7 +1091,7 @@ LLSD HttpCoroutineAdapter::copyAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
//
LLCore::HttpHandle hhandle = request->requestCopy(mPolicyId, mPriority, url,
LLCore::HttpHandle hhandle = request->requestCopy(mPolicyId, url,
options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)
@ -1140,7 +1133,7 @@ LLSD HttpCoroutineAdapter::moveAndSuspend_(LLCore::HttpRequest::ptr_t &request,
// The HTTPCoroHandler does not self delete, so retrieval of a the contained
// pointer from the smart pointer is safe in this case.
//
LLCore::HttpHandle hhandle = request->requestMove(mPolicyId, mPriority, url,
LLCore::HttpHandle hhandle = request->requestMove(mPolicyId, url,
options, headers, handler);
if (hhandle == LLCORE_HTTP_HANDLE_INVALID)

Some files were not shown because too many files have changed in this diff Show More