From 7122308534a65262f86c12ac45ac357dce8a6f4a Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 27 Feb 2012 15:24:39 -0800 Subject: SH-1998 : Compatible API changes KDU v6.4.1 / v7.0.0 --- indra/llkdu/llimagej2ckdu.cpp | 4 ++-- indra/llkdu/llimagej2ckdu.h | 1 + indra/llkdu/llkdumem.h | 1 + indra/llkdu/tests/llimagej2ckdu_test.cpp | 11 ++++++++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index c156ed0cef..6d55727abf 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -987,7 +987,7 @@ LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap) llassert(mDims == comp_dims); // Safety check; the caller has ensured this } bool use_shorts = (mComps[c].get_bit_depth(true) <= 16); - mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts); + mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts,0,0); if (res.which() == 0) // No DWT levels used { mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts); @@ -1031,7 +1031,7 @@ separation between consecutive rows in the real buffer. */ { for (c = 0; c < mNumComponents; c++) { - mEngines[c].pull(mLines[c],true); + mEngines[c].pull(mLines[c]); } if ((mNumComponents >= 3) && mUseYCC) { diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index 1489dbf704..84a4131da3 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -32,6 +32,7 @@ // // KDU core header files // +#define KDU_NO_THREADS #include "kdu_elementary.h" #include "kdu_messaging.h" #include "kdu_params.h" diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 9d923fc367..dbdf88b2d9 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,6 +28,7 @@ #define LL_LLKDUMEM_H // Support classes for reading and writing from memory buffers in KDU +#define KDU_NO_THREADS #include "kdu_image.h" #include "kdu_elementary.h" #include "kdu_messaging.h" diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index ab60ab6d50..697815625f 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -117,7 +117,7 @@ bool kdu_tile_comp::get_reversible() { return false; } kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } void kdu_resolution::get_dims(kdu_dims& ) { } int kdu_resolution::which() { return 0; } -kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*, int) { } kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } kdu_params::~kdu_params() { } @@ -141,7 +141,7 @@ void kdu_codestream::destroy() { } void kdu_codestream::collect_timing_stats(int ) { } void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { } void kdu_codestream::get_valid_tiles(kdu_dims& ) { } -void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long, kdu_thread_env* ) { } void kdu_codestream::create(kdu_compressed_source*, kdu_thread_env*) { } void kdu_codestream::apply_input_restrictions( int, int, int, int, kdu_dims*, kdu_component_access_mode ) { } void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } @@ -154,7 +154,12 @@ kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; bool kdu_codestream_comment::put_text(const char*) { return false; } void kdu_customize_warnings(kdu_message*) { } void kdu_customize_errors(kdu_message*) { } -void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } +//void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } +void (*kdu_convert_ycc_to_rgb_rev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_irrev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_rev32)(kdu_int32*,kdu_int32*,kdu_int32*,int); +void (*kdu_convert_ycc_to_rgb_irrev32)(float*,float*,float*,int); + kdu_long kdu_multi_analysis::create(kdu_codestream, kdu_tile, bool, kdu_roi_image*, bool, int, kdu_thread_env*, kdu_thread_queue*, bool ) { kdu_long a = 0; return a; } siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { } void siz_params::finalize(bool ) { } -- cgit v1.2.3 From bab6875f92422c01a0fb822496b7ae36638fc0ce Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 27 Feb 2012 17:43:48 -0800 Subject: SH-2998 : Move Mac and Windows to use KDU 7.0.0 --- autobuild.xml | 8 ++++---- indra/llkdu/tests/llimagej2ckdu_test.cpp | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 9914be6867..045bf54a0f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -966,9 +966,9 @@ archive hash - d91e1f483209cd3eba04135c6a59e829 + dd87a5ffe89b01e6f60a4b9bb13baf99 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/221672/arch/Darwin/installer/kdu-6.4.1-darwin-20110218.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/250131/arch/Darwin/installer/kdu-7.0.0-darwin-20120227.tar.bz2 name darwin @@ -990,9 +990,9 @@ archive hash - 6cd9f36465ef73a3df34bf2b3bba2ced + 0516ba3cf240d9e0b8e134c75a64de7a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/221672/arch/CYGWIN/installer/kdu-6.4.1-windows-20110218.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/250131/arch/CYGWIN/installer/kdu-7.0.0-windows-20120227.tar.bz2 name windows diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index 697815625f..eb3cad200d 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -117,7 +117,6 @@ bool kdu_tile_comp::get_reversible() { return false; } kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } void kdu_resolution::get_dims(kdu_dims& ) { } int kdu_resolution::which() { return 0; } -kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*, int) { } kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } kdu_params::~kdu_params() { } @@ -141,7 +140,6 @@ void kdu_codestream::destroy() { } void kdu_codestream::collect_timing_stats(int ) { } void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { } void kdu_codestream::get_valid_tiles(kdu_dims& ) { } -void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long, kdu_thread_env* ) { } void kdu_codestream::create(kdu_compressed_source*, kdu_thread_env*) { } void kdu_codestream::apply_input_restrictions( int, int, int, int, kdu_dims*, kdu_component_access_mode ) { } void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } @@ -154,11 +152,6 @@ kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; bool kdu_codestream_comment::put_text(const char*) { return false; } void kdu_customize_warnings(kdu_message*) { } void kdu_customize_errors(kdu_message*) { } -//void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } -void (*kdu_convert_ycc_to_rgb_rev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); -void (*kdu_convert_ycc_to_rgb_irrev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); -void (*kdu_convert_ycc_to_rgb_rev32)(kdu_int32*,kdu_int32*,kdu_int32*,int); -void (*kdu_convert_ycc_to_rgb_irrev32)(float*,float*,float*,int); kdu_long kdu_multi_analysis::create(kdu_codestream, kdu_tile, bool, kdu_roi_image*, bool, int, kdu_thread_env*, kdu_thread_queue*, bool ) { kdu_long a = 0; return a; } siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { } @@ -168,6 +161,21 @@ int siz_params::write_marker_segment(kdu_output*, kdu_params*, int) { return 0; bool siz_params::check_marker_segment(kdu_uint16, int, kdu_byte a[], int&) { return false; } bool siz_params::read_marker_segment(kdu_uint16, int, kdu_byte a[], int) { return false; } +#ifdef LL_LINUX +// Linux use the old pre KDU v7.0.0 +// *TODO: Supress this legacy stubbs once Linux migrates to v7.0.0 +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { } +void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { } +#else +kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*, int) { } +void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long, kdu_thread_env* ) { } +void (*kdu_convert_ycc_to_rgb_rev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_irrev16)(kdu_int16*,kdu_int16*,kdu_int16*,int); +void (*kdu_convert_ycc_to_rgb_rev32)(kdu_int32*,kdu_int32*,kdu_int32*,int); +void (*kdu_convert_ycc_to_rgb_irrev32)(float*,float*,float*,int); +#endif + // ------------------------------------------------------------------------------------------- // TUT // ------------------------------------------------------------------------------------------- -- cgit v1.2.3 From 5b80b00dcd3cf4e2dfec7dcd3e808866aad49095 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Mon, 16 Apr 2012 22:01:27 -0400 Subject: storm-1843: remove unused ParcelMediaURLFilter capability --- indra/newview/llviewerparcelmgr.cpp | 64 ------------------------------------- indra/newview/llviewerparcelmgr.h | 5 --- indra/newview/llviewerregion.cpp | 1 - 3 files changed, 70 deletions(-) diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 9db784101d..368b7f611c 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1662,9 +1662,6 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use // Request access list information for this land parcel_mgr.sendParcelAccessListRequest(AL_ACCESS | AL_BAN); - // Request the media url filter list for this land - parcel_mgr.requestParcelMediaURLFilter(); - // Request dwell for this land, if it's not public land. parcel_mgr.mSelectedDwell = DWELL_NAN; if (0 != local_id) @@ -1993,67 +1990,6 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) } } -class LLParcelMediaURLFilterResponder : public LLHTTPClient::Responder -{ - virtual void result(const LLSD& content) - { - LLViewerParcelMgr::getInstance()->receiveParcelMediaURLFilter(content); - } -}; - -void LLViewerParcelMgr::requestParcelMediaURLFilter() -{ - if (!mSelected) - { - return; - } - - LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth ); - if (!region) - { - return; - } - - LLParcel* parcel = mCurrentParcel; - if (!parcel) - { - llwarns << "no parcel" << llendl; - return; - } - - LLSD body; - body["local-id"] = parcel->getLocalID(); - body["list"] = parcel->getMediaURLFilterList(); - - std::string url = region->getCapability("ParcelMediaURLFilterList"); - if (!url.empty()) - { - LLHTTPClient::post(url, body, new LLParcelMediaURLFilterResponder); - } - else - { - llwarns << "can't get ParcelMediaURLFilterList cap" << llendl; - } -} - - -void LLViewerParcelMgr::receiveParcelMediaURLFilter(const LLSD &content) -{ - if (content.has("list")) - { - LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel; - if (!parcel) return; - - if (content["local-id"].asInteger() == parcel->getLocalID()) - { - parcel->setMediaURLFilterList(content["list"]); - - LLViewerParcelMgr::getInstance()->notifyObservers(); - } - } -} - - void LLViewerParcelMgr::deedLandToGroup() { std::string group_name; diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index cac8d8391c..b5b8fdf873 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -223,11 +223,6 @@ public: // Takes an Access List flag, like AL_ACCESS or AL_BAN void sendParcelAccessListRequest(U32 flags); - // asks for the parcel's media url filter list - void requestParcelMediaURLFilter(); - // receive the response - void receiveParcelMediaURLFilter(const LLSD &content); - // Dwell is not part of the usual parcel update information because the // simulator doesn't actually know the per-parcel dwell. Ack! We have // to get it out of the database. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e3cb985ddb..a43a6c6d7d 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1522,7 +1522,6 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("MeshUploadFlag"); capabilityNames.append("NewFileAgentInventory"); capabilityNames.append("ParcelPropertiesUpdate"); - capabilityNames.append("ParcelMediaURLFilterList"); capabilityNames.append("ParcelNavigateMedia"); capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); -- cgit v1.2.3 From 5611cb6d476540e6a1c654c1f9acdce2787b3505 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Apr 2012 16:19:39 -0400 Subject: Okay, imported the core-http library and got it compiling suspiciously easily. The unit/integration tests don't work yet as I'm still battling cmake/autobuild as usual but first milestone passed. --- indra/CMakeLists.txt | 1 + indra/llcorehttp/CMakeLists.txt | 112 ++++++ indra/llcorehttp/_assert.h | 39 ++ indra/llcorehttp/_httplibcurl.cpp | 214 ++++++++++ indra/llcorehttp/_httplibcurl.h | 89 +++++ indra/llcorehttp/_httpoperation.cpp | 203 ++++++++++ indra/llcorehttp/_httpoperation.h | 164 ++++++++ indra/llcorehttp/_httpoprequest.cpp | 473 +++++++++++++++++++++++ indra/llcorehttp/_httpoprequest.h | 127 ++++++ indra/llcorehttp/_httppolicy.cpp | 76 ++++ indra/llcorehttp/_httppolicy.h | 68 ++++ indra/llcorehttp/_httpreplyqueue.cpp | 87 +++++ indra/llcorehttp/_httpreplyqueue.h | 106 +++++ indra/llcorehttp/_httprequestqueue.cpp | 132 +++++++ indra/llcorehttp/_httprequestqueue.h | 104 +++++ indra/llcorehttp/_httpservice.cpp | 209 ++++++++++ indra/llcorehttp/_httpservice.h | 162 ++++++++ indra/llcorehttp/_mutex.h | 55 +++ indra/llcorehttp/_refcounted.cpp | 39 ++ indra/llcorehttp/_refcounted.h | 141 +++++++ indra/llcorehttp/_thread.h | 106 +++++ indra/llcorehttp/bufferarray.cpp | 281 ++++++++++++++ indra/llcorehttp/bufferarray.h | 168 ++++++++ indra/llcorehttp/httpcommon.cpp | 76 ++++ indra/llcorehttp/httpcommon.h | 204 ++++++++++ indra/llcorehttp/httphandler.h | 88 +++++ indra/llcorehttp/httpheaders.cpp | 44 +++ indra/llcorehttp/httpheaders.h | 86 +++++ indra/llcorehttp/httpoptions.cpp | 50 +++ indra/llcorehttp/httpoptions.h | 80 ++++ indra/llcorehttp/httprequest.cpp | 295 ++++++++++++++ indra/llcorehttp/httprequest.h | 316 +++++++++++++++ indra/llcorehttp/httpresponse.cpp | 89 +++++ indra/llcorehttp/httpresponse.h | 137 +++++++ indra/llcorehttp/tests/all_test.cpp | 64 +++ indra/llcorehttp/tests/test_allocator.cpp | 178 +++++++++ indra/llcorehttp/tests/test_allocator.h | 47 +++ indra/llcorehttp/tests/test_bufferarray.hpp | 456 ++++++++++++++++++++++ indra/llcorehttp/tests/test_httpheaders.hpp | 108 ++++++ indra/llcorehttp/tests/test_httpoperation.hpp | 128 ++++++ indra/llcorehttp/tests/test_httprequest.hpp | 437 +++++++++++++++++++++ indra/llcorehttp/tests/test_httprequestqueue.hpp | 186 +++++++++ indra/llcorehttp/tests/test_httpstatus.hpp | 163 ++++++++ indra/llcorehttp/tests/test_refcounted.hpp | 156 ++++++++ 44 files changed, 6544 insertions(+) create mode 100644 indra/llcorehttp/CMakeLists.txt create mode 100644 indra/llcorehttp/_assert.h create mode 100644 indra/llcorehttp/_httplibcurl.cpp create mode 100644 indra/llcorehttp/_httplibcurl.h create mode 100644 indra/llcorehttp/_httpoperation.cpp create mode 100644 indra/llcorehttp/_httpoperation.h create mode 100644 indra/llcorehttp/_httpoprequest.cpp create mode 100644 indra/llcorehttp/_httpoprequest.h create mode 100644 indra/llcorehttp/_httppolicy.cpp create mode 100644 indra/llcorehttp/_httppolicy.h create mode 100644 indra/llcorehttp/_httpreplyqueue.cpp create mode 100644 indra/llcorehttp/_httpreplyqueue.h create mode 100644 indra/llcorehttp/_httprequestqueue.cpp create mode 100644 indra/llcorehttp/_httprequestqueue.h create mode 100644 indra/llcorehttp/_httpservice.cpp create mode 100644 indra/llcorehttp/_httpservice.h create mode 100644 indra/llcorehttp/_mutex.h create mode 100644 indra/llcorehttp/_refcounted.cpp create mode 100644 indra/llcorehttp/_refcounted.h create mode 100644 indra/llcorehttp/_thread.h create mode 100644 indra/llcorehttp/bufferarray.cpp create mode 100644 indra/llcorehttp/bufferarray.h create mode 100644 indra/llcorehttp/httpcommon.cpp create mode 100644 indra/llcorehttp/httpcommon.h create mode 100644 indra/llcorehttp/httphandler.h create mode 100644 indra/llcorehttp/httpheaders.cpp create mode 100644 indra/llcorehttp/httpheaders.h create mode 100644 indra/llcorehttp/httpoptions.cpp create mode 100644 indra/llcorehttp/httpoptions.h create mode 100644 indra/llcorehttp/httprequest.cpp create mode 100644 indra/llcorehttp/httprequest.h create mode 100644 indra/llcorehttp/httpresponse.cpp create mode 100644 indra/llcorehttp/httpresponse.h create mode 100644 indra/llcorehttp/tests/all_test.cpp create mode 100644 indra/llcorehttp/tests/test_allocator.cpp create mode 100644 indra/llcorehttp/tests/test_allocator.h create mode 100644 indra/llcorehttp/tests/test_bufferarray.hpp create mode 100644 indra/llcorehttp/tests/test_httpheaders.hpp create mode 100644 indra/llcorehttp/tests/test_httpoperation.hpp create mode 100644 indra/llcorehttp/tests/test_httprequest.hpp create mode 100644 indra/llcorehttp/tests/test_httprequestqueue.hpp create mode 100644 indra/llcorehttp/tests/test_httpstatus.hpp create mode 100644 indra/llcorehttp/tests/test_refcounted.hpp diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 4b1bf49d07..031ffb26db 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) +add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llkdu) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt new file mode 100644 index 0000000000..8d21007ca0 --- /dev/null +++ b/indra/llcorehttp/CMakeLists.txt @@ -0,0 +1,112 @@ +# -*- cmake -*- + +project(llcorehttp) + +include(00-Common) +include(LLCoreHttp) +include(GoogleMock) +include(LLAddBuildTest) +include(LLCommon) +include(Tut) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} + ) + +set(llcorehttp_SOURCE_FILES + bufferarray.cpp + httpcommon.cpp + httprequest.cpp + httpresponse.cpp + httpoptions.cpp + httpheaders.cpp + _httprequestqueue.cpp + _httpoperation.cpp + _httpoprequest.cpp + _httpreplyqueue.cpp + _httppolicy.cpp + _httplibcurl.cpp + _httpservice.cpp + _refcounted.cpp + ) + +set(llcorehttp_HEADER_FILES + CMakeLists.txt + + bufferarray.h + httpcommon.h + httphandler.h + httpheaders.h + httpoptions.h + httprequest.h + httpresponse.h + _httpoperation.h + _httpoprequest.h + _httprequestqueue.h + _httpreplyqueue.h + _httpservice.h + _httppolicy.h + _httplibcurl.h + _assert.h + _refcounted.h + _mutex.h + _thread.h + ) + +set_source_files_properties(${llcorehttp_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) + +add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) +target_link_libraries( + llcorehttp + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ) + +# tests +#if (LL_TESTS) +if (LL_TESTS AND 0) + SET(llcorehttp_TEST_SOURCE_FILES + test_allocator.cpp + ) + + set(llcorehttp_TEST_HEADER_FILS + test_httpstatus.hpp + test_refcounted.hpp + test_httpoperation.hpp + test_httprequest.hpp + test_httprequestqueue.hpp + test_httpheaders.hpp + test_bufferarray.hpp + ) + + set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + + list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES}) + + # LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}") + + # set(TEST_DEBUG on) + set(test_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ) + + LL_ADD_INTEGRATION_TEST(all + "${llcorehttp_TEST_SOURCE_FILES}" + "${test_libs}" + ) + +#endif (LL_TESTS) +endif (LL_TESTS AND 0) + diff --git a/indra/llcorehttp/_assert.h b/indra/llcorehttp/_assert.h new file mode 100644 index 0000000000..054f23ef32 --- /dev/null +++ b/indra/llcorehttp/_assert.h @@ -0,0 +1,39 @@ +/** + * @file _assert + * @brief assert abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__ASSERT_H_ +#define LLCOREINT__ASSERT_H_ + +#ifdef DEBUG_ASSERT +#include +#define LLINT_ASSERT(x) assert(x) +#else +#define LLINT_ASSERT(x) +#endif + +#endif // LLCOREINT__ASSERT_H_ + + diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp new file mode 100644 index 0000000000..15be977adf --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -0,0 +1,214 @@ +/** + * @file _httplibcurl.cpp + * @brief Internal definitions of the Http libcurl thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httplibcurl.h" + +#include "httpheaders.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpLibcurl::HttpLibcurl(HttpService * service) + : mService(service) +{ + mMultiHandles[0] = curl_multi_init(); +} + + +HttpLibcurl::~HttpLibcurl() +{ + // *FIXME: need to cancel requests in this class, not in op class. + while (! mActiveOps.empty()) + { + active_set_t::iterator item(mActiveOps.begin()); + + (*item)->cancel(); + (*item)->release(); + } + + if (mMultiHandles[0]) + { + // *FIXME: Do some multi cleanup here first + + + curl_multi_cleanup(mMultiHandles[0]); + mMultiHandles[0] = NULL; + } + + mService = NULL; +} + + +void HttpLibcurl::init() +{} + + +void HttpLibcurl::term() +{} + + +void HttpLibcurl::processTransport() +{ + if (mMultiHandles[0]) + { + // Give libcurl some cycles to do I/O & callbacks + int running(0); + CURLMcode status(CURLM_CALL_MULTI_PERFORM); + do + { + running = 0; + status = curl_multi_perform(mMultiHandles[0], &running); + } + while (0 != running && CURLM_CALL_MULTI_PERFORM == status); + + // Run completion on anything done + CURLMsg * msg(NULL); + int msgs_in_queue(0); + while ((msg = curl_multi_info_read(mMultiHandles[0], &msgs_in_queue))) + { + if (CURLMSG_DONE == msg->msg) + { + CURL * handle(msg->easy_handle); + CURLcode result(msg->data.result); + + completeRequest(mMultiHandles[0], handle, result); + handle = NULL; // No longer valid on return + } + else if (CURLMSG_NONE == msg->msg) + { + // Ignore this... it shouldn't mean anything. + ; + } + else + { + // *FIXME: Issue a logging event for this. + ; + } + msgs_in_queue = 0; + } + } +} + + +void HttpLibcurl::addOp(HttpOpRequest * op) +{ + // Create standard handle + if (! op->prepareForGet(mService)) + { + // Couldn't issue request, fail with notification + // *FIXME: Need failure path + return; + } + + // Make the request live + curl_multi_add_handle(mMultiHandles[0], op->mCurlHandle); + op->mCurlActive = true; + + // On success, make operation active + mActiveOps.insert(op); +} + + +void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + if (handle != op->mCurlHandle || ! op->mCurlActive) + { + // *FIXME: This is a sanity check that needs validation/termination. + ; + } + + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + // *FIXME: Fatal condition. This must be here. + ; + } + else + { + mActiveOps.erase(it); + } + + // Deactivate request + op->mCurlActive = false; + + // Set final status of request + if (op->mStatus) + { + // Only set if it hasn't failed by other mechanisms yet + op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); + } + if (op->mStatus) + { + int http_status(200); + + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); + op->mReplyStatus = http_status; + } + + // Detach from multi and recycle handle + curl_multi_remove_handle(multi_handle, handle); + curl_easy_cleanup(handle); + op->mCurlHandle = NULL; + + // Deliver to reply queue and release + op->stageFromActive(mService); + op->release(); +} + + +int HttpLibcurl::activeCount() const +{ + return mActiveOps.size(); +} + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +{ + for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); + headers->mHeaders.end() != it; + ++it) + { + slist = curl_slist_append(slist, (*it).c_str()); + } + return slist; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h new file mode 100644 index 0000000000..5ba244cee4 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.h @@ -0,0 +1,89 @@ +/** + * @file _httplibcurl.h + * @brief Declarations for internal class providing libcurl transport. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_LIBCURL_H_ +#define _LLCORE_HTTP_LIBCURL_H_ + + +#include +#include + +#include + + +namespace LLCore +{ + + +class HttpService; +class HttpPolicy; +class HttpOpRequest; +class HttpHeaders; + + +class HttpLibcurl +{ +public: + HttpLibcurl(HttpService * service); + virtual ~HttpLibcurl(); + +private: + HttpLibcurl(const HttpLibcurl &); // Not defined + void operator=(const HttpLibcurl &); // Not defined + +public: + static void init(); + static void term(); + + void processTransport(); + void addOp(HttpOpRequest * op); + + int activeCount() const; + +protected: + void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + +protected: + typedef std::set active_set_t; + +protected: + HttpService * mService; + active_set_t mActiveOps; + CURLM * mMultiHandles[1]; +}; // end class HttpLibcurl + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_LIBCURL_H_ diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp new file mode 100644 index 0000000000..17c65b0379 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.cpp @@ -0,0 +1,203 @@ +/** + * @file _httpoperation.cpp + * @brief Definitions for internal classes based on HttpOperation + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoperation.h" + +#include "httphandler.h" +#include "httpresponse.h" +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOperation +// ================================== + + +HttpOperation::HttpOperation() + : LLCoreInt::RefCounted(true), + mReplyQueue(NULL), + mLibraryHandler(NULL), + mUserHandler(NULL), + mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), + mReqPriority(0.0f) +{ +} + + +HttpOperation::~HttpOperation() +{ + setHandlers(NULL, NULL, NULL); +} + + +void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, + HttpHandler * lib_handler, + HttpHandler * user_handler) +{ + if (reply_queue != mReplyQueue) + { + if (mReplyQueue) + { + mReplyQueue->release(); + } + + if (reply_queue) + { + reply_queue->addRef(); + } + + mReplyQueue = reply_queue; + } + + // Not refcounted + mLibraryHandler = lib_handler; + + // Not refcounted + mUserHandler = user_handler; +} + + + +void HttpOperation::stageFromRequest(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromReady(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::stageFromActive(HttpService *) +{ + // *FIXME: Message this a better way later. + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LLINT_ASSERT(false); +} + + +void HttpOperation::visitNotifier(HttpRequest *) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + + mLibraryHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOperation::cancel() +{ + HttpStatus status; + + return status; +} + + +void HttpOperation::addAsReply() +{ + if (mReplyQueue && mLibraryHandler) + { + addRef(); + mReplyQueue->addOp(this); + } +} + + +// ================================== +// HttpOpStop +// ================================== + + +HttpOpStop::HttpOpStop() + : HttpOperation() +{} + + +HttpOpStop::~HttpOpStop() +{} + + +void HttpOpStop::stageFromRequest(HttpService * service) +{ + // Do operations + service->stopRequested(); + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpNull +// ================================== + + +HttpOpNull::HttpOpNull() + : HttpOperation() +{} + + +HttpOpNull::~HttpOpNull() +{} + + +void HttpOpNull::stageFromRequest(HttpService * service) +{ + // Perform op + // Nothing to perform. This doesn't fall into the libcurl + // ready/active queues, it just bounces over to the reply + // queue directly. + + // Prepare response if needed + addAsReply(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h new file mode 100644 index 0000000000..d04961c47b --- /dev/null +++ b/indra/llcorehttp/_httpoperation.h @@ -0,0 +1,164 @@ +/** + * @file _httpoperation.h + * @brief Internal declarations for HttpOperation and sub-classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPERATION_H_ +#define _LLCORE_HTTP_OPERATION_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class HttpReplyQueue; +class HttpHandler; +class HttpService; +class HttpRequest; + +/// HttpOperation is the base class for all request/reply +/// pairs. +/// +class HttpOperation : public LLCoreInt::RefCounted +{ +public: + HttpOperation(); + virtual ~HttpOperation(); + +private: + HttpOperation(const HttpOperation &); // Not defined + void operator=(const HttpOperation &); // Not defined + +public: + void setHandlers(HttpReplyQueue * reply_queue, + HttpHandler * lib_handler, + HttpHandler * user_handler); + + HttpHandler * getUserHandler() const + { + return mUserHandler; + } + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest *); + + virtual HttpStatus cancel(); + +protected: + void addAsReply(); + +protected: + HttpReplyQueue * mReplyQueue; // Have refcount + HttpHandler * mLibraryHandler; // Have refcount + HttpHandler * mUserHandler; // Have refcount + +public: + unsigned int mReqPolicy; + float mReqPriority; + +}; // end class HttpOperation + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. Requests that have been made +/// active and are available for sending on the wire cannot +/// be canceled. + +class HttpOpCancel : public HttpOperation +{ +public: + HttpOpCancel(); + virtual ~HttpOpCancel(); + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + +public: + HttpHandle mHandle; +}; // end class HttpOpCancel + + +/// HttpOpStop requests the servicing thread to shutdown +/// operations, cease pulling requests from the request +/// queue and release shared resources (particularly +/// those shared via reference count). The servicing +/// thread will then exit. The underlying thread object +/// remains so that another thread can join on the +/// servicing thread prior to final cleanup. The +/// request *does* generate a reply on the response +/// queue, if requested. + +class HttpOpStop : public HttpOperation +{ +public: + HttpOpStop(); + virtual ~HttpOpStop(); + +private: + HttpOpStop(const HttpOpStop &); // Not defined + void operator=(const HttpOpStop &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpStop + + +/// HttpOpNull is a do-nothing operation used for testing via +/// a basic loopback pattern. It's executed immediately by +/// the servicing thread which bounces a reply back to the +/// caller without any further delay. + +class HttpOpNull : public HttpOperation +{ +public: + HttpOpNull(); + virtual ~HttpOpNull(); + +private: + HttpOpNull(const HttpOpNull &); // Not defined + void operator=(const HttpOpNull &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpNull + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPERATION_H_ + diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp new file mode 100644 index 0000000000..d71ace5d57 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -0,0 +1,473 @@ +/** + * @file _httpoprequest.cpp + * @brief Definitions for internal class HttpOpRequest + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoprequest.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" +#include "httpoptions.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace +{ + +// Attempts to parse a 'Content-Range:' header. Caller must already +// have verified that the header tag is present. The 'buffer' argument +// will be processed by strtok_r calls which will modify the buffer. +// +// @return -1 if invalid and response should be dropped, 0 if valid an +// correct, 1 if couldn't be parsed. If 0, the first, last, +// and length arguments are also written. 'length' may be +// 0 if the length wasn't available to the server. +// +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length); + +#if defined(WIN32) + +// Not available on windows where the legacy strtok interface +// is thread-safe. +char *strtok_r(char *str, const char *delim, char **saveptr); + +#endif + +} + + +namespace LLCore +{ + + +// ================================== +// HttpOpRequest +// ================================== + + +HttpOpRequest::HttpOpRequest() + : HttpOperation(), + mProcFlags(0U), + mReqMethod(HOR_GET), + mReqBody(NULL), + mReqOffset(0), + mReqLen(0), + mReqHeaders(NULL), + mReqOptions(NULL), + mCurlActive(false), + mCurlHandle(NULL), + mCurlHeaders(NULL), + mCurlService(NULL), + mReplyStatus(200), + mReplyBody(NULL), + mReplyOffset(0), + mReplyLen(0), + mReplyHeaders(NULL) +{} + + +HttpOpRequest::~HttpOpRequest() +{ + if (mCurlHandle) + { + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + mCurlService = NULL; + + if (mReqBody) + { + mReqBody->release(); + mReqBody = NULL; + } + + if (mReqHeaders) + { + curl_slist_free_all(mReqHeaders); + mReqHeaders = NULL; + } + + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } +} + + +void HttpOpRequest::stageFromRequest(HttpService * service) +{ + addRef(); + service->getPolicy()->addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromReady(HttpService * service) +{ + addRef(); + service->getTransport()->addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromActive(HttpService * service) +{ + if (mReplyLen) + { + // If non-zero, we received and processed a Content-Range + // header with the response. Verify that what it says + // is consistent with the received data. + if (mReplyLen != mReplyBody->size()) + { + // Not as expected, fail the request + mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + } + + if (mReqHeaders) + { + // We take these headers out of the request now as they were + // allocated originally in this thread and the notifier doesn't + // need them. This eliminates one source of heap moving across + // threads. + + curl_slist_free_all(mReqHeaders); + mReqHeaders = NULL; + } + addAsReply(); +} + + +void HttpOpRequest::visitNotifier(HttpRequest * request) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + + // *FIXME: add http status, offset, length + response->setStatus(mStatus); + response->setReplyStatus(mReplyStatus); + response->setBody(mReplyBody); + response->setHeaders(mReplyHeaders); + mLibraryHandler->onCompleted(static_cast(this), response); + + response->release(); + } +} + + +HttpStatus HttpOpRequest::cancel() +{ + mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED); + + addAsReply(); + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_GET; + mReqURL = url; + mReqOffset = offset; + mReqLen = len; + if (offset || len) + { + mProcFlags |= PF_SCAN_RANGE_HEADER; + } + if (headers && ! mReqHeaders) + { + mReqHeaders = append_headers_to_slist(headers, mReqHeaders); + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + +HttpStatus HttpOpRequest::prepareForGet(HttpService * service) +{ + // *FIXME: better error handling later + HttpStatus status; + + mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + // curl_easy_setopt(handle, CURLOPT_PROXY, ""); + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); + curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + + if (mReqOffset || mReqLen) + { + static const char * fmt1("Range: bytes=%d-%d"); + static const char * fmt2("Range: bytes=%d-"); + + char range_line[64]; + +#if defined(WIN32) + _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, + (mReqLen ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLen - 1); +#else + snprintf(range_line, sizeof(range_line), + (mReqLen ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLen - 1); +#endif // defined(WIN32) + range_line[sizeof(range_line) - 1] = '\0'; + mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); + } + + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle); + } + + if (status) + { + mCurlService = service; + } + return status; +} + + +size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + CURL * handle(static_cast(userdata)); + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + if (! op->mReplyBody) + { + op->mReplyBody = new BufferArray(); + } + const size_t req_size(size * nmemb); + char * lump(op->mReplyBody->appendBufferAlloc(req_size)); + memcpy(lump, data, req_size); + + return req_size; +} + + +size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + 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:"; + static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + CURL * handle(static_cast(userdata)); + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + const size_t hdr_size(size * nmemb); + const char * hdr_data(static_cast(data)); // Not null terminated + + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) + { + // One of possibly several status lines. Reset what we know and start over + op->mReplyOffset = 0; + op->mReplyLen = 0; + op->mStatus = HttpStatus(); + } + else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + { + char hdr_buffer[128]; + size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); + + memcpy(hdr_buffer, hdr_data, frag_size); + hdr_buffer[frag_size] = '\0'; +#if defined(WIN32) + if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#else + if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) +#endif + { + unsigned int first(0), last(0), length(0); + int status; + + if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length))) + { + // Success, record the fragment position + op->mReplyOffset = first; + op->mReplyLen = last - first + 1; + } + else if (-1 == status) + { + // Response is badly formed and shouldn't be accepted + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + else + { + // Ignore the unparsable. + // *FIXME: Maybe issue a warning into the log here + ; + } + } + } + + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + ; + + } + + return hdr_size; +} + +} // end namespace LLCore + + +// ======================================= +// Anonymous Namespace +// ======================================= + +namespace +{ + +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length) +{ + char * tok_state(NULL), * tok; + bool match(true); + + if (! strtok_r(buffer, ": \t", &tok_state)) + match = false; + if (match && (tok = strtok_r(NULL, " \t", &tok_state))) +#if defined(WIN32) + match = 0 == _stricmp("bytes", tok); +#else + match = 0 == strcasecmp("bytes", tok); +#endif + if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) + match = false; + if (match) + { + unsigned int lcl_first(0), lcl_last(0), lcl_len(0); + +#if defined(WIN32) + if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#else + if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#endif + { + if (lcl_first > lcl_last || lcl_last >= lcl_len) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = lcl_len; + return 0; + } +#if defined(WIN32) + if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#else + if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#endif + { + if (lcl_first > lcl_last) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = 0; + return 0; + } + } + + // Header is there but badly/unexpectedly formed, try to ignore it. + return 1; +} + +#if defined(WIN32) + +char *strtok_r(char *str, const char *delim, char ** savestate) +{ + return strtok_s(str, delim, savestate); +} + +#endif + +} // end anonymous namespace + + diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h new file mode 100644 index 0000000000..e912851aed --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.h @@ -0,0 +1,127 @@ +/** + * @file _httpoprequest.h + * @brief Internal declarations for the HttpOpRequest subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPREQUEST_H_ +#define _LLCORE_HTTP_OPREQUEST_H_ + + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +class BufferArray; +class HttpHeaders; +class HttpOptions; + + +/// HttpOpRequest requests a supported HTTP method invocation with +/// option and header overrides. + +class HttpOpRequest : public HttpOperation +{ +public: + HttpOpRequest(); + virtual ~HttpOpRequest(); + +private: + HttpOpRequest(const HttpOpRequest &); // Not defined + void operator=(const HttpOpRequest &); // Not defined + +public: + enum EMethod + { + HOR_GET, + HOR_POST, + HOR_PUT + }; + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + // Setup Methods + HttpStatus setupGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus prepareForGet(HttpService * service); + + virtual HttpStatus cancel(); + +protected: + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + +protected: + unsigned int mProcFlags; + static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; + static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + +public: + // Request data + EMethod mReqMethod; + std::string mReqURL; + BufferArray * mReqBody; + off_t mReqOffset; + size_t mReqLen; + curl_slist * mReqHeaders; + HttpOptions * mReqOptions; + + // Transport data + bool mCurlActive; + CURL * mCurlHandle; + curl_slist * mCurlHeaders; + HttpService * mCurlService; + + // Result data + HttpStatus mStatus; + int mReplyStatus; + BufferArray * mReplyBody; + off_t mReplyOffset; + size_t mReplyLen; + HttpHeaders * mReplyHeaders; +}; // end class HttpOpRequest + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPREQUEST_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp new file mode 100644 index 0000000000..d965a6cf3a --- /dev/null +++ b/indra/llcorehttp/_httppolicy.cpp @@ -0,0 +1,76 @@ +/** + * @file _httppolicy.cpp + * @brief Internal definitions of the Http policy thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicy.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpPolicy::HttpPolicy(HttpService * service) + : mService(service) +{} + + +HttpPolicy::~HttpPolicy() +{ + for (ready_queue_t::reverse_iterator i(mReadyQueue.rbegin()); + mReadyQueue.rend() != i;) + { + ready_queue_t::reverse_iterator cur(i++); + + (*cur)->cancel(); + (*cur)->release(); + } + + mService = NULL; +} + + +void HttpPolicy::addOp(HttpOpRequest * op) +{ + mReadyQueue.push_back(op); +} + + +void HttpPolicy::processReadyQueue() +{ + while (! mReadyQueue.empty()) + { + HttpOpRequest * op(mReadyQueue.front()); + mReadyQueue.erase(mReadyQueue.begin()); + + op->stageFromReady(mService); + op->release(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h new file mode 100644 index 0000000000..28aea27f38 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.h @@ -0,0 +1,68 @@ +/** + * @file _httppolicy.h + * @brief Declarations for internal class enforcing policy decisions. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_H_ +#define _LLCORE_HTTP_POLICY_H_ + + +#include + + +namespace LLCore +{ + + +class HttpService; +class HttpOpRequest; + + +class HttpPolicy +{ +public: + HttpPolicy(HttpService *); + virtual ~HttpPolicy(); + +private: + HttpPolicy(const HttpPolicy &); // Not defined + void operator=(const HttpPolicy &); // Not defined + +public: + void processReadyQueue(); + + void addOp(HttpOpRequest *); + +protected: + typedef std::vector ready_queue_t; + +protected: + HttpService * mService; // Naked pointer, not refcounted + ready_queue_t mReadyQueue; + +}; // end class HttpPolicy + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_H_ diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp new file mode 100644 index 0000000000..a354ed7e10 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -0,0 +1,87 @@ +/** + * @file _httpreplyqueue.cpp + * @brief Internal definitions for the operation reply queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpreplyqueue.h" + + +#include "_mutex.h" +#include "_thread.h" +#include "_httpoperation.h" + +using namespace LLCoreInt; + + +namespace LLCore +{ + + +HttpReplyQueue::HttpReplyQueue() + : RefCounted(true) +{ +} + + +HttpReplyQueue::~HttpReplyQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpReplyQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpReplyQueue::fetchOp() +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + if (mQueue.empty()) + return NULL; + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h new file mode 100644 index 0000000000..56dadec87c --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -0,0 +1,106 @@ +/** + * @file _httpreplyqueue.h + * @brief Internal declarations for the operation reply queue. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REPLY_QUEUE_H_ +#define _LLCORE_HTTP_REPLY_QUEUE_H_ + + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Almost identical to the HttpRequestQueue class but +/// whereas that class is a singleton and is known to the +/// HttpService object, this queue is 1:1 with HttpRequest +/// instances and isn't explicitly referenced by the +/// service object. Instead, HttpOperation objects that +/// want to generate replies back to their creators also +/// keep references to the corresponding HttpReplyQueue. +/// The HttpService plumbing then simply delivers replies +/// to the requested reply queue. +/// +/// One result of that is that the fetch operations do +/// not have a wait forever option. The service object +/// doesn't keep handles on everything it would need to +/// notify so it can't wake up sleepers should it need to +/// shutdown. So only non-blocking or timed-blocking modes +/// are anticipated. These are how most application consumers +/// will be coded anyway so it shouldn't be too much of a +/// burden. + +class HttpReplyQueue: public LLCoreInt::RefCounted +{ +public: + /// Caller acquires a Refcount on construction + HttpReplyQueue(); + virtual ~HttpReplyQueue(); + +private: + HttpReplyQueue(const HttpReplyQueue &); // Not defined + void operator=(const HttpReplyQueue &); // Not defined + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Library also takes possession of one reference count to pass + /// through the queue. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Fetch an operation from the head of the queue. Returns + /// NULL if none exists. + /// + /// Caller acquires reference count on returned operation. + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(OpContainer & ops); + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpReplyQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REPLY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp new file mode 100644 index 0000000000..c36814aee3 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -0,0 +1,132 @@ +/** + * @file _httprequestqueue.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httprequestqueue.h" + +#include "_httpoperation.h" +#include "_mutex.h" + + +using namespace LLCoreInt; + +namespace LLCore +{ + +HttpRequestQueue * HttpRequestQueue::sInstance(NULL); + + +HttpRequestQueue::HttpRequestQueue() + : RefCounted(true) +{ +} + + +HttpRequestQueue::~HttpRequestQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpRequestQueue::init() +{ + LLINT_ASSERT(! sInstance); + sInstance = new HttpRequestQueue(); +} + + +void HttpRequestQueue::term() +{ + if (sInstance) + { + sInstance->release(); + sInstance = NULL; + } +} + + +void HttpRequestQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpRequestQueue::fetchOp(bool wait) +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait) + return NULL; + mQueueCV.wait(lock); + } + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) +{ + // Note: Should probably test whether we're empty or not here. + // A target passed in with entries is likely also carrying + // reference counts and we're going to leak something. + ops.clear(); + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait) + return; + mQueueCV.wait(lock); + } + + mQueue.swap(ops); + } + + // Caller also acquires the reference counts on each op. + return; +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h new file mode 100644 index 0000000000..3a9ce0c3c6 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.h @@ -0,0 +1,104 @@ +/** + * @file _httprequestqueue.h + * @brief Internal declaration for the operation request queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_QUEUE_H_ +#define _LLCORE_HTTP_REQUEST_QUEUE_H_ + + +#include + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Thread-safe queue of HttpOperation objects. Just +/// a simple queue that handles the transfer of operation +/// requests from all HttpRequest instances into the +/// singleton HttpService instance. + +class HttpRequestQueue: public LLCoreInt::RefCounted +{ +protected: + /// Caller acquires a Refcount on construction + HttpRequestQueue(); + virtual ~HttpRequestQueue(); + +private: + HttpRequestQueue(const HttpRequestQueue &); // Not defined + void operator=(const HttpRequestQueue &); // Not defined + +public: + static void init(); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpRequestQueue * instanceOf() + { + return sInstance; + } + +public: + typedef std::vector OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Caller my provide one refcount to the Library which takes + /// possession of the count. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Caller acquires reference count on returned operation + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(bool wait); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(bool wait, OpContainer & ops); + +protected: + static HttpRequestQueue * sInstance; + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpRequestQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp new file mode 100644 index 0000000000..6ebc0ec6cb --- /dev/null +++ b/indra/llcorehttp/_httpservice.cpp @@ -0,0 +1,209 @@ +/** + * @file _httpservice.cpp + * @brief Internal definitions of the Http service thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpservice.h" + +#include +#include + +#include "_httpoperation.h" +#include "_httprequestqueue.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" +#include "_thread.h" + + +namespace LLCore +{ + +HttpService * HttpService::sInstance(NULL); +volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); + +HttpService::HttpService() + : mRequestQueue(NULL), + mExitRequested(false), + mThread(NULL), + mPolicy(NULL), + mTransport(NULL) +{ +} + + +HttpService::~HttpService() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mPolicy) + { + // *TODO: need a finalization here + ; + } + + if (mTransport) + { + // *TODO: need a finalization here + delete mTransport; + mTransport = NULL; + } + + if (mPolicy) + { + delete mPolicy; + mPolicy = NULL; + } + + if (mThread) + { + mThread->release(); + mThread = NULL; + } +} + + +void HttpService::init(HttpRequestQueue * queue) +{ + LLINT_ASSERT(! sInstance); + LLINT_ASSERT(NOT_INITIALIZED == sState); + sInstance = new HttpService(); + + queue->addRef(); + sInstance->mRequestQueue = queue; + sInstance->mPolicy = new HttpPolicy(sInstance); + sInstance->mTransport = new HttpLibcurl(sInstance); + sState = INITIALIZED; +} + + +void HttpService::term() +{ + LLINT_ASSERT(RUNNING != sState); + if (sInstance) + { + delete sInstance; + sInstance = NULL; + } + sState = NOT_INITIALIZED; +} + + +bool HttpService::isStopped() +{ + // What is really wanted here is something like: + // + // HttpService * service = instanceOf(); + // return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable()); + // + // But boost::thread is not giving me a consistent story on joinability + // of a thread after it returns. Debug and non-debug builds are showing + // different behavior on Linux/Etch so we do a weaker test that may + // not be globally correct (i.e. thread *is* stopping, may not have + // stopped but will very soon): + + return STOPPED == sState; +} + + +void HttpService::startThread() +{ + LLINT_ASSERT(! mThread || STOPPED == sState); + LLINT_ASSERT(INITIALIZED == sState || STOPPED == sState); + + if (mThread) + { + mThread->release(); + } + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); + mThread->addRef(); // Need an explicit reference, implicit one is used internally + sState = RUNNING; +} + + +void HttpService::stopRequested() +{ + mExitRequested = true; +} + + +void HttpService::shutdown() +{ + // *FIXME: Run down everything.... +} + + +void HttpService::threadRun(LLCoreInt::HttpThread * thread) +{ + boost::this_thread::disable_interruption di; + + while (! mExitRequested) + { + processRequestQueue(); + + // Process ready queue issuing new requests as needed + mPolicy->processReadyQueue(); + + // Give libcurl some cycles + mTransport->processTransport(); + + // Determine whether to spin, sleep briefly or sleep for next request + // *FIXME: For now, do this +#if defined(WIN32) + Sleep(50); +#else + usleep(5000); +#endif + } + shutdown(); + sState = STOPPED; +} + + +void HttpService::processRequestQueue() +{ + HttpRequestQueue::OpContainer ops; + + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + // Process operation + if (! mExitRequested) + { + op->stageFromRequest(this); + } + + // Done with operation + op->release(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h new file mode 100644 index 0000000000..c052e35452 --- /dev/null +++ b/indra/llcorehttp/_httpservice.h @@ -0,0 +1,162 @@ +/** + * @file _httpservice.h + * @brief Declarations for internal class providing HTTP service. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_SERVICE_H_ +#define _LLCORE_HTTP_SERVICE_H_ + + +namespace LLCoreInt +{ + +class HttpThread; + +} + + +namespace LLCore +{ + + +class HttpRequestQueue; +class HttpPolicy; +class HttpLibcurl; + + +/// The HttpService class does the work behind the request queue. It +/// oversees the HTTP workflow carrying out a number of tasks: +/// - Pulling requests from the global request queue +/// - Executing 'immediate' requests directly +/// - Prioritizing and re-queuing on internal queues the slower requests +/// - Providing cpu cycles to the libcurl plumbing +/// - Overseeing retry operations +/// +/// Note that the service object doesn't have a pointer to any +/// reply queue. These are kept by HttpRequest and HttpOperation +/// only. +/// +/// Service, Policy and Transport +/// +/// HttpService could have been a monolithic class combining a request +/// queue servicer, request policy manager and network transport. +/// Instead, to prevent monolithic growth and allow for easier +/// replacement, it was developed as three separate classes: HttpService, +/// HttpPolicy and HttpLibcurl (transport). These always exist in a +/// 1:1:1 relationship with HttpService managing instances of the other +/// two. So, these classes do not use reference counting to refer +/// to one-another, their lifecycles are always managed together. + +class HttpService +{ +protected: + HttpService(); + virtual ~HttpService(); + +private: + HttpService(const HttpService &); // Not defined + void operator=(const HttpService &); // Not defined + +public: + enum EState + { + NOT_INITIALIZED = -1, + INITIALIZED, ///< init() has been called + RUNNING, ///< thread created and running + STOPPED ///< thread has committed to exiting + }; + + static void init(HttpRequestQueue *); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpService * instanceOf() + { + return sInstance; + } + + /// Return the state of the worker thread. Note that the + /// transition from RUNNING to STOPPED is performed by the + /// worker thread itself. This has two weaknesses: + /// - race where the thread hasn't really stopped but will + /// - data ordering between threads where a non-worker thread + /// may see a stale RUNNING status. + /// + /// This transition is generally of interest only to unit tests + /// and these weaknesses shouldn't be any real burden. + /// + /// Threading: callable by any thread with above exceptions. + static EState getState() + { + return sState; + } + + /// Threading: callable by any thread but uses @see getState() and + /// acquires its weaknesses. + static bool isStopped(); + + /// Threading: callable by application thread *once*. + void startThread(); + + /// Threading: callable by worker thread. + void stopRequested(); + + /// Threading: callable by worker thread. + void shutdown(); + + HttpPolicy * getPolicy() + { + return mPolicy; + } + + HttpLibcurl * getTransport() + { + return mTransport; + } + +protected: + void threadRun(LLCoreInt::HttpThread * thread); + + void processRequestQueue(); + +protected: + static HttpService * sInstance; + + // === shared data === + static volatile EState sState; + HttpRequestQueue * mRequestQueue; + volatile bool mExitRequested; + + // === calling-thread-only data === + LLCoreInt::HttpThread * mThread; + + // === working-thread-only data === + HttpPolicy * mPolicy; + HttpLibcurl * mTransport; + +}; // end class HttpService + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SERVICE_H_ diff --git a/indra/llcorehttp/_mutex.h b/indra/llcorehttp/_mutex.h new file mode 100644 index 0000000000..4be4d016d4 --- /dev/null +++ b/indra/llcorehttp/_mutex.h @@ -0,0 +1,55 @@ +/** + * @file _mutex.hpp + * @brief mutex type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_MUTEX_H_ +#define LLCOREINT_MUTEX_H_ + + +#include + + +namespace LLCoreInt +{ + +// MUTEX TYPES + +// unique mutex type +typedef boost::mutex HttpMutex; + +// CONDITION VARIABLES + +// standard condition variable +typedef boost::condition_variable HttpConditionVariable; + +// LOCKS AND FENCES + +// scoped unique lock +typedef boost::unique_lock HttpScopedLock; + +} + +#endif // LLCOREINT_MUTEX_H + diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp new file mode 100644 index 0000000000..2c132e1b36 --- /dev/null +++ b/indra/llcorehttp/_refcounted.cpp @@ -0,0 +1,39 @@ +/** + * @file _refcounted.cpp + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +RefCounted::~RefCounted() +{} + + +} // end namespace LLCoreInt + + diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h new file mode 100644 index 0000000000..4a6ce8420a --- /dev/null +++ b/indra/llcorehttp/_refcounted.h @@ -0,0 +1,141 @@ +/** + * @file _refcounted.h + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__REFCOUNTED_H_ +#define LLCOREINT__REFCOUNTED_H_ + + +#include + +#include "_assert.h" + + +namespace LLCoreInt +{ + + +class RefCounted +{ +private: + RefCounted(); // Not defined - may not be default constructed + void operator=(const RefCounted &); // Not defined + +public: + explicit RefCounted(bool const implicit) + : mRefCount(implicit) + {} + + // ref-count interface + void addRef() const; + void release() const; + bool isLastRef() const; + int getRefCount() const; + void noRef() const; + + static const int NOT_REF_COUNTED = -1; + +protected: + virtual ~RefCounted(); + virtual void destroySelf(); + +private: + mutable int mRefCount; + mutable boost::mutex mRefLock; + +}; // end class RefCounted + + +inline void RefCounted::addRef() const +{ + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount >= 0); + ++mRefCount; +} + + +inline void RefCounted::release() const +{ + int count(0); + { + // CRITICAL SECTION + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); + LLINT_ASSERT(mRefCount > 0); + count = --mRefCount; + // CRITICAL SECTION + } + + + // clean ourselves up if that was the last reference + if (0 == count) + { + const_cast(this)->destroySelf(); + } +} + + +inline bool RefCounted::isLastRef() const +{ + int count(0); + { + // CRITICAL SECTION + boost::mutex::scoped_lock lock(mRefLock); + + LLINT_ASSERT(mRefCount != NOT_REF_COUNTED); + LLINT_ASSERT(mRefCount >= 1); + count = mRefCount; + // CRITICAL SECTION + } + + return (1 == count); +} + + +inline int RefCounted::getRefCount() const +{ + boost::mutex::scoped_lock lock(mRefLock); + const int result(mRefCount); + return result; +} + + +inline void RefCounted::noRef() const +{ + boost::mutex::scoped_lock lock(mRefLock); + LLINT_ASSERT(mRefCount <= 1); + mRefCount = NOT_REF_COUNTED; +} + + +inline void RefCounted::destroySelf() +{ + delete this; +} + +} // end namespace LLCoreInt + +#endif // LLCOREINT__REFCOUNTED_H_ + diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h new file mode 100644 index 0000000000..5960f0dcdb --- /dev/null +++ b/indra/llcorehttp/_thread.h @@ -0,0 +1,106 @@ +/** + * @file _thread.h + * @brief thread type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_THREAD_H_ +#define LLCOREINT_THREAD_H_ + + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +class HttpThread : public RefCounted +{ +private: + HttpThread(); // Not defined + void operator=(const HttpThread &); // Not defined + + void at_exit() + { + // the thread function has exited so we need to release our reference + // to ourself so that we will be automagically cleaned up. + release(); + } + + void run() + { // THREAD CONTEXT + + // The implicit reference to this object is taken for the at_exit + // function so that the HttpThread instance doesn't disappear out + // from underneath it. Other holders of the object may want to + // take a reference as well. + boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); + + // run the thread function + mThreadFunc(this); + + } // THREAD CONTEXT + +public: + /// Constructs a thread object for concurrent execution but does + /// not start running. Unlike other classes that mixin RefCounted, + /// this does take out a reference but it is used internally for + /// final cleanup during at_exit processing. Callers needing to + /// keep a reference must increment it themselves. + /// + explicit HttpThread(boost::function threadFunc) + : RefCounted(true), // implicit reference + mThreadFunc(threadFunc) + { + // this creates a boost thread that will call HttpThread::run on this instance + // and pass it the threadfunc callable... + boost::function f = boost::bind(&HttpThread::run, this); + + mThread = new boost::thread(f); + } + + virtual ~HttpThread() + { + delete mThread; + } + + inline void join() + { + mThread->join(); + } + + inline bool joinable() const + { + mThread->joinable(); + } + +private: + boost::function mThreadFunc; + boost::thread * mThread; +}; // end class HttpThread + +} // end namespace LLCoreInt + +#endif // LLCOREINT_THREAD_H_ + + diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000..4c20350b13 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,281 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferarray.h" + + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: + ~Block(); + + void operator delete(void *); + void operator delete(void *, size_t len); + +protected: + Block(size_t len); + + Block(const Block &); // Not defined + void operator=(const Block &); // Not defined + + // Allocate the block with the additional space for the + // buffered data at the end of the object. + void * operator new(size_t len, size_t addl_len); + +public: + // Only public entry to get a block. + static Block * alloc(size_t len); + +public: + size_t mLen; + + // *NOTE: Must be last member of the object. We'll + // overallocate as requested via operator new and index + // into the array at will. + char mData[1]; +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +BufferArray::BufferArray() + : LLCoreInt::RefCounted(true), + mPos(0), + mLen(0) +{} + + +BufferArray::~BufferArray() +{ + for (container_t::iterator it(mBlocks.begin()); + it != mBlocks.end(); + ++it) + { + delete *it; + *it = NULL; + } + mBlocks.clear(); +} + + +size_t BufferArray::append(const char * src, size_t len) +{ + if (len) + { + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + memcpy(block->mData, src, len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + } + return len; +} + + +char * BufferArray::appendBufferAlloc(size_t len) +{ + // If someone asks for zero-length, we give them a valid pointer. + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(len); + mBlocks.push_back(block); + mLen += len; + mPos = mLen; + return block->mData; +} + + +size_t BufferArray::seek(size_t pos) +{ + if (pos > mLen) + pos = mLen; + mPos = pos; + return mPos; +} + + +size_t BufferArray::read(char * dst, size_t len) +{ + size_t result(0), offset(0); + size_t len_limit(mLen - mPos); + len = std::min(len, len_limit); + + if (mPos >= mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + if (block_start < 0) + return 0; + + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(dst, &block.mData[offset], block_len); + result += block_len; + len -= block_len; + dst += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + + mPos += result; + return result; +} + + +size_t BufferArray::write(const char * src, size_t len) +{ + size_t result(0), offset(0); + if (mPos > mLen || 0 == len) + return 0; + + const int block_limit(mBlocks.size()); + int block_start(findBlock(mPos, &offset)); + + if (block_start >= 0) + { + // Some or all of the write will be on top of + // existing data. + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mLen - offset); + size_t block_len(std::min(block_limit, len)); + + memcpy(&block.mData[offset], src, block_len); + result += block_len; + len -= block_len; + src += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + } + mPos += result; + + if (len) + { + // Some or all of the remaining write data will + // be an append. + result += append(src, len); + } + + return result; +} + + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ + *ret_offset = 0; + if (pos >= mLen) + return -1; // Doesn't exist + + const int block_limit(mBlocks.size()); + for (int i(0); i < block_limit; ++i) + { + if (pos < mBlocks[i]->mLen) + { + *ret_offset = pos; + return i; + } + pos -= mBlocks[i]->mLen; + } + + // Shouldn't get here but... + return -1; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) + : mLen(len) +{ + memset(mData, 0, len); +} + + +BufferArray::Block::~Block() +{ + mLen = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ + void * mem = new char[len + addl_len + sizeof(void *)]; + return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ + char * cmem = static_cast(mem); + delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ + operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ + Block * block = new (len) Block(len); + return block; +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h new file mode 100644 index 0000000000..b26ad1b297 --- /dev/null +++ b/indra/llcorehttp/bufferarray.h @@ -0,0 +1,168 @@ +/** + * @file bufferarray.h + * @brief Public-facing declaration for the BufferArray scatter/gather class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_ARRAY_H_ +#define _LLCORE_BUFFER_ARRAY_H_ + + +#include +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// A very simple scatter/gather type map for bulk data. The motivation +/// for this class is the writedata callback used by libcurl. Response +/// bodies are delivered to the caller in a sequence of sequential write +/// operations and this class captures them without having to reallocate +/// and move data. +/// +/// The interface looks a little like a unix file descriptor but only +/// just. There is a notion of a current position, starting from 0, +/// which is used as the position in the data when performing read and +/// write operations. The position also moves after various operations: +/// - seek(...) +/// - read(...) +/// - write(...) +/// - append(...) +/// - appendBufferAlloc(...) +/// The object also keeps a total length value which is updated after +/// write and append operations and beyond which the current position +/// cannot be set. +/// +/// Threading: not thread-safe +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a single refcount. +/// +class BufferArray : public LLCoreInt::RefCounted +{ +public: + BufferArray(); + virtual ~BufferArray(); + +private: + BufferArray(const BufferArray &); // Not defined + void operator=(const BufferArray &); // Not defined + +public: + /// Appends the indicated data to the BufferArray + /// modifying current position and total size. New + /// position is one beyond the final byte of the buffer. + /// + /// @return Count of bytes copied to BufferArray + size_t append(const char * src, size_t len); + + /// Similar to @see append(), this call guarantees a + /// contiguous block of memory of requested size placed + /// at the current end of the BufferArray. On return, + /// the data in the memory is considered valid whether + /// the caller writes to it or not. + /// + /// @return Pointer to contiguous region at end + /// of BufferArray of 'len' size. + char * appendBufferAlloc(size_t len); + + /// Current count of bytes in BufferArray instance. + size_t size() const + { + return mLen; + } + + /// Set the current position for subsequent read and + /// write operations. 'pos' values before the beginning + /// or greater than the size of the buffer are coerced + /// to a value within the buffer. + /// + /// @return Actual current position after seek. + size_t seek(size_t pos); + + /// Copies data from the current position in the instance + /// to the caller's buffer. Will return a short count of + /// bytes copied if the 'len' extends beyond the data. + size_t read(char * dst, size_t len); + + /// Copies data from the caller's buffer to the instance + /// at the current position. May overwrite existing data, + /// append data when current position is equal to the + /// size of the instance or do a mix of both. + size_t write(const char * src, size_t len); + +protected: + int findBlock(size_t pos, size_t * ret_offset); + +protected: + class Block; + typedef std::vector container_t; + + container_t mBlocks; + size_t mPos; + size_t mLen; +}; // end class BufferArray + + +#if 0 + +// Conceptual for now. Another possibility is going with +// something like Boost::asio's buffers interface. They're +// trying to achieve the same thing above and below.... + +class BufferStream : public std::streambuf +{ +public: + BufferStream(BufferArray * buffer); + virtual ~BufferStream(); + +private: + BufferStream(const BufferStream &); // Not defined + void operator=(const BufferStream &); // Not defined + +public: + // Types + typedef std::streambuf::pos_type pos_type; + typedef std::streambuf::off_type off_type; + + virtual int underflow(); + + virtual int overflow(int c); + + virtual int sync(); + + virtual pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which); + +protected: + BufferArray * mBufferArray; +}; // end class BufferStream + +#endif // 0 + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp new file mode 100644 index 0000000000..c37d081150 --- /dev/null +++ b/indra/llcorehttp/httpcommon.cpp @@ -0,0 +1,76 @@ +/** + * @file httpcommon.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpcommon.h" + +#include +#include + + +namespace LLCore +{ + +HttpStatus::type_enum_t EXT_CURL_EASY; +HttpStatus::type_enum_t EXT_CURL_MULTI; +HttpStatus::type_enum_t LLCORE; + +std::string HttpStatus::toString() const +{ + static const char * llcore_errors[] = + { + "", + "Services shutting down", + "Operation canceled", + "Invalid Content-Range header encountered" + }; + static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + if (*this) + { + return std::string(""); + } + + switch (mType) + { + case EXT_CURL_EASY: + return std::string(curl_easy_strerror(CURLcode(mStatus))); + + case EXT_CURL_MULTI: + return std::string(curl_multi_strerror(CURLMcode(mStatus))); + + case LLCORE: + if (mStatus >= 0 && mStatus < llcore_errors_count) + { + return std::string(llcore_errors[mStatus]); + } + break; + + } + return std::string("Unknown error"); +} + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h new file mode 100644 index 0000000000..617286fb38 --- /dev/null +++ b/indra/llcorehttp/httpcommon.h @@ -0,0 +1,204 @@ +/** + * @file httpcommon.h + * @brief Public-facing declarations and definitions of common types + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_COMMON_H_ +#define _LLCORE_HTTP_COMMON_H_ + +/// @package LLCore::HTTP +/// +/// This library implements a high-level, Indra-code-free client interface to +/// HTTP services based on actual patterns found in the viewer and simulator. +/// Interfaces are similar to those supplied by the legacy classes +/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that +/// allows an application to specify connection behaviors: limits on +/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. +/// +/// Features of the library include: +/// - Single, private working thread where all transport and processing occurs. +/// - Support for multiple consumers running in multiple threads. +/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. +/// - Reference counting used for many object instance lifetimes. +/// - Minimal data sharing across threads for correctness and low latency. +/// +/// The public interface is declared in a few key header files: +/// - +/// - +/// - +/// - +/// - +/// - +/// - +/// +/// The library is still under early development and particular users +/// may need access to internal implementation details that are found +/// in the _*.h header files. But this is a crutch to be avoided if at +/// all possible and probably indicates some interface work is neeeded. +/// +/// Using the library is fairly easy. Global setup needs a few +/// steps: +/// +/// - libcurl initialization with thread-safely callbacks for c-ares +/// DNS lookups. +/// - HttpRequest::createService() called to instantiate singletons +/// and support objects. +/// +/// An HTTP consumer in an application, and an application may have many +/// consumers, does a few things: +/// +/// - Instantiate and retain an object based on HttpRequest. This +/// object becomes the portal into runtime services for the consumer. +/// - Derive or mixin the HttpHandler class if you want notification +/// when requests succeed or fail. This object's onCompleted() +/// method is invoked and an instance can be shared across +/// requests. +/// +/// Issuing a request is straightforward: +/// - Construct a suitable URL. +/// - Configure HTTP options for the request. (optional) +/// - Build a list of additional headers. (optional) +/// - Invoke one of the requestXXXX() methods (requestGetByteRange, +/// requestPost, etc.) on the HttpRequest instance supplying the +/// above along with a policy class, a priority and an optional +/// pointer to an HttpHandler instance. Work is then queued to +/// the worker thread and occurs asynchronously. +/// - Periodically invoke the update() method on the HttpRequest +/// instance which performs completion notification to HttpHandler +/// objects. +/// - Do completion processing in your onCompletion() method. +/// +/// Code fragments: +/// +/// + +#include + + +namespace LLCore +{ + + +/// All queued requests are represented by an HttpHandle value. +/// The invalid value is returned when a request failed to queue. +/// The actual status for these failures is then fetched with +/// HttpRequest::getStatus(). +/// +/// The handle is valid only for the life of a request. On +/// return from any HttpHandler notification, the handle immediately +/// becomes invalid and may be recycled for other queued requests. + +typedef void * HttpHandle; +#define LLCORE_HTTP_HANDLE_INVALID (NULL) + + +/// Error codes defined by the library itself as distinct from +/// libcurl (or any other transport provider). +enum HttpError +{ + // Successful value compatible with the libcurl codes. + HE_SUCCESS = 0, + + // Service is shutting down and requested operation will + // not be queued or performed. + HE_SHUTTING_DOWN = 1, + + // Operation was canceled by request. + HE_OP_CANCELED = 2, + + // Invalid content range header received. + HE_INV_CONTENT_RANGE_HDR = 3 + +}; // end enum HttpError + + +/// HttpStatus encapsulates errors from libcurl (easy, multi) as well as +/// internal errors. The encapsulation isn't expected to completely +/// isolate the caller from libcurl but basic operational tests (success +/// or failure) are provided. +struct HttpStatus +{ + typedef unsigned short type_enum_t; + + HttpStatus() + : mType(LLCORE), + mStatus(HE_SUCCESS) + {} + + HttpStatus(type_enum_t type, short status) + : mType(type), + mStatus(status) + {} + + HttpStatus(const HttpStatus & rhs) + : mType(rhs.mType), + mStatus(rhs.mStatus) + {} + + HttpStatus & operator=(const HttpStatus & rhs) + { + // Don't care if lhs & rhs are the same object + + mType = rhs.mType; + mStatus = rhs.mStatus; + return *this; + } + + static const type_enum_t EXT_CURL_EASY = 0; + static const type_enum_t EXT_CURL_MULTI = 1; + static const type_enum_t LLCORE = 2; + + type_enum_t mType; + short mStatus; + + /// Test for successful status in the code regardless + /// of error source (internal, libcurl). + /// + /// @return 'true' when status is successful. + /// + operator bool() const + { + return 0 == mStatus; + } + + /// Inverse of previous operator. + /// + /// @return 'true' on any error condition + bool operator !() const + { + return 0 != mStatus; + } + + /// Convert status to a string representation. For + /// success, returns an empty string. For failure + /// statuses, a string as appropriate for the source of + /// the error code (libcurl easy, libcurl multi, or + /// LLCore itself). + std::string toString() const; + +}; // end struct HttpStatus + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h new file mode 100644 index 0000000000..9171e4e7b9 --- /dev/null +++ b/indra/llcorehttp/httphandler.h @@ -0,0 +1,88 @@ +/** + * @file httphandler.h + * @brief Public-facing declarations for the HttpHandler class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HANDLER_H_ +#define _LLCORE_HTTP_HANDLER_H_ + + +#include "httpcommon.h" + + +namespace LLCore +{ + +class HttpResponse; + + +/// HttpHandler defines an interface used by the library to +/// notify library callers of significant events, currently +/// request completion. Callers must derive or mixin this class +/// then provide an implementation of the @see onCompleted +/// method to receive such notifications. An instance may +/// be shared by any number of requests and across instances +/// of HttpRequest running in the same thread. +/// +/// Threading: HttpHandler itself is pure interface and is +/// tread-compatible. Most derivations, however, will have +/// different constraints. +/// +/// Allocation: Not refcounted, may be stack allocated though +/// that is rarely a good idea. Queued requests and replies keep +/// a naked pointer to the handler and this can result in a +/// dangling pointer if lifetimes aren't managed correctly. + +class HttpHandler +{ +public: + virtual ~HttpHandler() + {} + + /// Method invoked during calls to @see update(). Each invocation + /// represents the completion of some requested operation. Caller + /// can identify the request from the handle and interrogate the + /// response argument for success/failure, data and other information. + /// + /// @param handle Identifier of the request generating + /// the notification. + /// @param response Supplies detailed information about + /// the request including status codes + /// (both programming and HTTP), HTTP body + /// data and encodings, headers, etc. + /// The response object is refcounted and + /// the called code may retain the object + /// by invoking @see addRef() on it. The + /// library itself drops all references to + /// to object on return and never touches + /// it again. + /// + virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0; + +}; // end class HttpHandler + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_HANDLER_H_ diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp new file mode 100644 index 0000000000..2832696271 --- /dev/null +++ b/indra/llcorehttp/httpheaders.cpp @@ -0,0 +1,44 @@ +/** + * @file httpheaders.cpp + * @brief Implementation of the HTTPHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpHeaders::HttpHeaders() + : RefCounted(true) +{} + + +HttpHeaders::~HttpHeaders() +{} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h new file mode 100644 index 0000000000..0b6d82561b --- /dev/null +++ b/indra/llcorehttp/httpheaders.h @@ -0,0 +1,86 @@ +/** + * @file httpheaders.h + * @brief Public-facing declarations for the HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HEADERS_H_ +#define _LLCORE_HTTP_HEADERS_H_ + + +#include + +#include "_refcounted.h" + + +namespace LLCore +{ + +/// +/// Maintains an ordered list of name/value pairs representing +/// HTTP header lines. This is used both to provide additional +/// headers when making HTTP requests and in responses when the +/// caller has asked that headers be returned (not the default +/// option). +/// +/// @note +/// This is a minimally-functional placeholder at the moment +/// to fill out the class hierarchy. The final class will be +/// something else, probably more pair-oriented. It's also +/// an area where shared values are desirable so refcounting is +/// already specced and a copy-on-write scheme imagined. +/// Expect changes here. +/// +/// Threading: Not intrinsically thread-safe. It *is* expected +/// that callers will build these objects and then share them +/// via reference counting with the worker thread. The implication +/// is that once an HttpHeader instance is handed to a request, +/// the object must be treated as read-only. +/// +/// Allocation: Refcounted, heap only. Caller of the +/// constructor is given a refcount. +/// + +class HttpHeaders : public LLCoreInt::RefCounted +{ +public: + /// @post In addition to the instance, caller has a refcount + /// to the instance. A call to @see release() will destroy + /// the instance. + HttpHeaders(); + ~HttpHeaders(); + +protected: + HttpHeaders(const HttpHeaders &); // Not defined + void operator=(const HttpHeaders &); // Not defined + +public: + typedef std::vector container_t; + container_t mHeaders; + +}; // end class HttpHeaders + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp new file mode 100644 index 0000000000..15b505f5bf --- /dev/null +++ b/indra/llcorehttp/httpoptions.cpp @@ -0,0 +1,50 @@ +/** + * @file httpoptions.cpp + * @brief Implementation of the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpoptions.h" + + +namespace LLCore +{ + + +HttpOptions::HttpOptions() + : RefCounted(true), + mWantHeaders(false) +{} + + +HttpOptions::HttpOptions(const HttpOptions & rhs) + : RefCounted(true), + mWantHeaders(rhs.mWantHeaders) +{} + + +HttpOptions::~HttpOptions() +{} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h new file mode 100644 index 0000000000..267a982dd5 --- /dev/null +++ b/indra/llcorehttp/httpoptions.h @@ -0,0 +1,80 @@ +/** + * @file httpoptions.h + * @brief Public-facing declarations for the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPTIONS_H_ +#define _LLCORE_HTTP_OPTIONS_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// Really a struct in spirit, it provides options that +/// modify HTTP requests. +/// +/// Sharing instances across requests. It's intended that +/// these be shared across requests: caller can create one +/// of these, set it up as needed and then reference it +/// repeatedly in HTTP operations. But see the Threading +/// note about references. +/// +/// Threading: While this class does nothing to ensure thread +/// safety, it *is* intended to be shared between the application +/// thread and the worker thread. This means that once an instance +/// is delivered to the library in request operations, the +/// option data must not be written until all such requests +/// complete and relinquish their references. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpOptions : public LLCoreInt::RefCounted +{ +public: + HttpOptions(); + HttpOptions(const HttpOptions &); + virtual ~HttpOptions(); + +protected: + void operator=(const HttpOptions &); // Not defined + +public: + +protected: + // *TODO: add some options + bool mWantHeaders; + +}; // end class HttpOptions + + +} // end namespace HttpOptions + +#endif // _LLCORE_HTTP_OPTIONS_H_ diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp new file mode 100644 index 0000000000..2a87f5231a --- /dev/null +++ b/indra/llcorehttp/httprequest.cpp @@ -0,0 +1,295 @@ +/** + * @file httprequest.cpp + * @brief Implementation of the HTTPRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httpoperation.h" +#include "_httpoprequest.h" + + +namespace +{ + +bool has_inited(false); + +} + +namespace LLCore +{ + +// ==================================== +// InternalHandler Implementation +// ==================================== + + +class HttpRequest::InternalHandler : public HttpHandler +{ +public: + InternalHandler(HttpRequest & request) + : mRequest(request) + {} + +protected: + InternalHandler(const InternalHandler &); // Not defined + void operator=(const InternalHandler &); // Not defined + +public: + void onCompleted(HttpHandle handle, HttpResponse * response) + { + HttpOperation * op(static_cast(handle)); + HttpHandler * user_handler(op->getUserHandler()); + if (user_handler) + { + user_handler->onCompleted(handle, response); + } + } + +protected: + HttpRequest & mRequest; + +}; // end class HttpRequest::InternalHandler + + +// ==================================== +// HttpRequest Implementation +// ==================================== + + +unsigned int HttpRequest::sNextPolicyID(1); + + +HttpRequest::HttpRequest() + : //HttpHandler(), + mReplyQueue(NULL), + mRequestQueue(NULL), + mSelfHandler(NULL) +{ + mRequestQueue = HttpRequestQueue::instanceOf(); + mRequestQueue->addRef(); + + mReplyQueue = new HttpReplyQueue(); + + mSelfHandler = new InternalHandler(*this); +} + + +HttpRequest::~HttpRequest() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mReplyQueue) + { + mReplyQueue->release(); + mReplyQueue = NULL; + } + + delete mSelfHandler; + mSelfHandler = NULL; +} + + +// ==================================== +// Policy Methods +// ==================================== + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +{ + HttpStatus status; + + return status; +} + + +unsigned int HttpRequest::createPolicyClass() +{ + unsigned int policy_id = 1; + + return policy_id; +} + + +HttpStatus HttpRequest::setPolicyClassOption(unsigned int policy_id, + EClassPolicy opt, + long value) +{ + HttpStatus status; + + return status; +} + + +// ==================================== +// Request Methods +// ==================================== + + +HttpStatus HttpRequest::getStatus() const +{ + return mLastReqStatus; +} + + +HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpNull * op = new HttpOpNull(); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +HttpStatus HttpRequest::update(long millis) +{ + HttpStatus status; + + // *FIXME: need timer stuff + // long now(getNow()); + // long limit(now + millis); + + HttpOperation * op(NULL); + while ((op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + + return status; +} + + + + +// ==================================== +// Request Management Methods +// ==================================== + + +// ==================================== +// Utility Methods +// ==================================== + +HttpStatus HttpRequest::createService() +{ + HttpStatus status; + + LLINT_ASSERT(! has_inited); + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + + return status; +} + + +HttpStatus HttpRequest::destroyService() +{ + HttpStatus status; + + LLINT_ASSERT(has_inited); + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + + return status; +} + + +HttpStatus HttpRequest::startThread() +{ + HttpStatus status; + + HttpService::instanceOf()->startThread(); + + return status; +} + + +HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpStop * op = new HttpOpStop(); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h new file mode 100644 index 0000000000..4bbd13a13a --- /dev/null +++ b/indra/llcorehttp/httprequest.h @@ -0,0 +1,316 @@ +/** + * @file httprequest.h + * @brief Public-facing declarations for HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_H_ +#define _LLCORE_HTTP_REQUEST_H_ + + +#include "httpcommon.h" +#include "httphandler.h" + + +namespace LLCore +{ + +class HttpRequestQueue; +class HttpReplyQueue; +class HttpService; +class HttpOptions; +class HttpHeaders; +class HttpOperation; + +/// HttpRequest supplies the entry into the HTTP transport +/// services in the LLCore libraries. Services provided include: +/// +/// - Some, but not all, global initialization of libcurl. +/// - Starting asynchronous, threaded HTTP requests. +/// - Definition of policy classes affect request handling. +/// - Utilities to control request options and headers +/// +/// Requests +/// +/// The class supports the current HTTP request operations: +/// +/// - requestGetByteRange: GET with Range header for a single range of bytes +/// +/// Policy Classes +/// +/// +/// +/// Usage +/// +/// +/// +/// Threading: An instance may only be used by one application/ +/// consumer thread. But a thread may have as many instances of +/// this as it likes. +/// +/// Allocation: Not refcounted, may be stack allocated though that +/// hasn't been tested. Queued requests can still run and any +/// queued replies will keep refcounts to the reply queue leading +/// to memory leaks. +/// +/// @pre Before using this class (static or instances), some global +/// initialization is required. See @see httpcommon.h for more information. +/// +/// @nosubgrouping +/// + +class HttpRequest +{ +public: + HttpRequest(); + virtual ~HttpRequest(); + +private: + HttpRequest(const HttpRequest &); // Disallowed + void operator=(const HttpRequest &); // Disallowed + +public: + /// @name PolicyMethods + /// @{ + + /// Represents a default, catch-all policy class that guarantees + /// eventual service for any HTTP request. + static const int DEFAULT_POLICY_ID = 0; + + enum EGlobalPolicy + { + /// Maximum number of connections the library will use to + /// perform operations. This is somewhat soft as the underlying + /// transport will cache some connections (up to 5). + GLOBAL_CONNECTION_LIMIT + }; + + /// Set a parameter on a global policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + + /// Create a new policy class into which requests can be made. + /// + /// @return If positive, the policy_id used to reference + /// the class in other methods. If -1, an error + /// occurred and @see getStatus() may provide more + /// detail on the reason. + unsigned int createPolicyClass(); + + enum EClassPolicy + { + /// Limits the number of connections used for the class. + CLASS_CONNECTION_LIMIT, + + /// Limits the number of connections used for a single + /// literal address/port pair within the class. + PER_HOST_CONNECTION_LIMIT, + + /// Suitable requests are allowed to pipeline on their + /// connections when they ask for it. + ENABLE_PIPELINING + }; + + /// Set a parameter on a class-based policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param policy_id ID of class as returned by @see createPolicyClass(). + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + HttpStatus setPolicyClassOption(unsigned int policy_id, + EClassPolicy opt, + long value); + + /// @} + + /// @name RequestMethods + /// + /// @{ + + /// Some calls expect to succeed as the normal part of operation and so + /// return a useful value rather than a status. When they do fail, the + /// status is saved and can be fetched with this method. + /// + /// @return Status of the failing method invocation. If the + /// preceding call succeeded or other HttpStatus + /// returning calls immediately preceded this method, + /// the returned value may not be reliable. + /// + HttpStatus getStatus() const; + + /// Queue a full HTTP GET request to be issued with a 'Range' header. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// @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. + /// @param url + /// @param offset + /// @param len + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGetByteRange(unsigned int policy_id, + float priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a NoOp request. + /// The request is queued and serviced by the working thread which + /// immediately processes it and returns the request to the reply + /// queue. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestNoOp(HttpHandler * handler); + + /// While all the heavy work is done by the worker thread, notifications + /// must be performed in the context of the application thread. These + /// are done synchronously during calls to this method which gives the + /// library control so notification can be performed. Application handlers + /// are expected to return 'quickly' and do any significant processing + /// outside of the notification callback to onCompleted(). + /// + /// @param millis Maximum number of wallclock milliseconds to + /// spend in the call. As hinted at above, this + /// is partly a function of application code so it's + /// a soft limit. (And not currently implemented.) + /// + /// @return Standard status code. + HttpStatus update(long millis); + + /// @} + + /// @name RequestMgmtMethods + /// + /// @{ + + HttpHandle requestCancel(HttpHandle request, HttpHandler *); + + /// @} + + /// @name UtilityMethods + /// + /// @{ + + /// Initialization method that needs to be called before queueing any + /// requests. Doesn't start the worker thread and may be called befoer + /// or after policy setup. + static HttpStatus createService(); + + /// Mostly clean shutdown of services prior to exit. Caller is expected + /// to have stopped a running worker thread before calling this. + static HttpStatus destroyService(); + + /// Called once after @see createService() to start the worker thread. + /// Stopping the thread is achieved by requesting it via @see requestStopThread(). + /// May be called before or after requests are issued. + static HttpStatus startThread(); + + /// Queues a request to the worker thread to have it stop processing + /// and exit (without exiting the program). When the operation is + /// picked up by the worker thread, it immediately processes it and + /// begins detaching from refcounted resources like request and + /// reply queues and then returns to the host OS. It *does* queue a + /// reply to give the calling application thread a notification that + /// the operation has been performed. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// As the request cannot be cancelled, the handle + /// is generally not useful. + /// + HttpHandle requestStopThread(HttpHandler * handler); + + /// @} + +protected: + void generateNotification(HttpOperation * op); + + class InternalHandler; + friend class InternalHandler; + +private: + /// @name InstanceData + /// + /// @{ + HttpStatus mLastReqStatus; + HttpReplyQueue * mReplyQueue; + HttpRequestQueue * mRequestQueue; + InternalHandler * mSelfHandler; + + /// @} + + // ==================================== + /// @name GlobalState + /// + /// @{ + /// + /// Must be established before any threading is allowed to + /// start. + /// + static unsigned int sNextPolicyID; + + /// @} + // End Global State + // ==================================== + +}; // end class HttpRequest + + +} // end namespace LLCore + + + +#endif // _LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp new file mode 100644 index 0000000000..9ac8276f05 --- /dev/null +++ b/indra/llcorehttp/httpresponse.cpp @@ -0,0 +1,89 @@ +/** + * @file httpresponse.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpResponse::HttpResponse() + : LLCoreInt::RefCounted(true), + mReplyStatus(0U), + mBufferArray(NULL), + mHeaders(NULL) +{} + + +HttpResponse::~HttpResponse() +{ + setBody(NULL); + setHeaders(NULL); +} + + +void HttpResponse::setBody(BufferArray * ba) +{ + if (mBufferArray == ba) + return; + + if (mBufferArray) + { + mBufferArray->release(); + } + + if (ba) + { + ba->addRef(); + } + + mBufferArray = ba; +} + + +void HttpResponse::setHeaders(HttpHeaders * headers) +{ + if (mHeaders == headers) + return; + + if (mHeaders) + { + mHeaders->release(); + } + + if (headers) + { + headers->addRef(); + } + + mHeaders = headers; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h new file mode 100644 index 0000000000..a25e22aef6 --- /dev/null +++ b/indra/llcorehttp/httpresponse.h @@ -0,0 +1,137 @@ +/** + * @file httpresponse.h + * @brief Public-facing declarations for the HttpResponse class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RESPONSE_H_ +#define _LLCORE_HTTP_RESPONSE_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArray; +class HttpHeaders; + +/// HttpResponse is instantiated by the library and handed to +/// the caller during callbacks to the handler. It supplies +/// all the status, header and HTTP body data the caller is +/// interested in. Methods provide simple getters to return +/// individual pieces of the response. +/// +/// Typical usage will have the caller interrogate the object +/// and return from the handler callback. Instances are refcounted +/// and callers can bump the count and retain the object as needed. +/// +/// Threading: Not intrinsically thread-safe. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpResponse : public LLCoreInt::RefCounted +{ +public: + HttpResponse(); + virtual ~HttpResponse(); + +protected: + HttpResponse(const HttpResponse &); // Not defined + void operator=(const HttpResponse &); // Not defined + +public: + /// Returns the final status of the requested operation. + /// + // *FIXME: Haven't incorporated HTTP status into this yet. + // Will do soon. + HttpStatus getStatus() const + { + return mStatus; + } + + void setStatus(const HttpStatus & status) + { + mStatus = status; + } + + /// Fetch the HTTP reply status. This is only guaranteed to be + /// valid if the HttpStatus tests successful and was the result + /// of a completed HTTP request. + unsigned int getReplyStatus() const + { + return mReplyStatus; + } + + void setReplyStatus(unsigned int status) + { + mReplyStatus = status; + } + + /// Simple getter for the response body returned as a scatter/gather + /// buffer. If the operation doesn't produce data (such as the Null + /// or StopThread operations), this may be NULL. + /// + /// Caller can hold onto the response by incrementing the reference + /// count of the returned object. + BufferArray * getBody() const + { + return mBufferArray; + } + + /// Set the response data in the instance. Will drop the reference + /// count to any existing data and increment the count of that passed + /// in. It is legal to set the data to NULL. + void setBody(BufferArray * ba); + + /// And a getter for the headers. And as with @see getResponse(), + /// if headers aren't available because the operation doesn't produce + /// any or delivery of headers wasn't requested in the options, this + /// will be NULL. + /// + /// Caller can hold onto the headers by incrementing the reference + /// count of the returned object. + HttpHeaders * getHeaders() const + { + return mHeaders; + } + + /// Behaves like @see setResponse() but for header data. + void setHeaders(HttpHeaders * headers); + +protected: + // Response data here + HttpStatus mStatus; + unsigned int mReplyStatus; + BufferArray * mBufferArray; + HttpHeaders * mHeaders; +}; + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_RESPONSE_H_ diff --git a/indra/llcorehttp/tests/all_test.cpp b/indra/llcorehttp/tests/all_test.cpp new file mode 100644 index 0000000000..636f6f8c05 --- /dev/null +++ b/indra/llcorehttp/tests/all_test.cpp @@ -0,0 +1,64 @@ +/** + * @file test_all + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include + +#include +#include + +#include + +// Pull in each of the test sets +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_bufferarray.hpp" +#include "test_httprequestqueue.hpp" + + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100644 index 0000000000..926de0a208 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,178 @@ +/** + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include +#elif defined(_MSC_VER) +#include +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include +#include +#include +#include +#include +#include + +#include + + +#if defined(WIN32) +#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc) +#define THROW_NOTHING() _THROW0() +#else +#define THROW_BAD_ALLOC() throw(std::bad_alloc) +#define THROW_NOTHING() throw() +#endif + + +struct BlockHeader +{ + struct Block * next; + std::size_t size; + bool in_use; +}; + +struct Block +{ + BlockHeader hdr; + unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast(static_cast(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) + return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 + return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ + // TRACE_MSG(size); + volatile Block * pBlock = NULL; + volatile Block * pNewNext = NULL; + + // do a lock-free update of the global next pointer + do + { + pBlock = pNext; + pNewNext = (volatile Block *)(pBlock->data + size); + + } while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + + // if we get here, we safely carved out a block of memory in the + // memory pool... + + // initialize our block + pBlock->hdr.next = (Block *)(pBlock->data + size); + pBlock->hdr.size = size; + pBlock->hdr.in_use = true; + memset((void *) pBlock->data, 0, pBlock->hdr.size); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total + size; + + } while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + + return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ + // get the pointer to the block record + Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + + // TRACE_MSG(pBlock->hdr.size); + bool * cur_in_use = &(pBlock->hdr.in_use); + volatile bool in_use = false; + bool new_in_use = false; + do + { + in_use = pBlock->hdr.in_use; + } while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total - pBlock->hdr.size; + } while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ + return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ + FreeMem( p ); +} + + +void operator delete[](void * p) THROW_NOTHING() +{ + FreeMem( p ); +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100644 index 0000000000..3572bbc5c5 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/** + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include +#include + +size_t GetMemTotal(); +#if defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100644 index 0000000000..4f5d0284a1 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,456 @@ +/** + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct BufferArrayTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ + set_test_name("BufferArray construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in BA", 0 == ba->size()); + + // Try to read + char buffer[20]; + size_t read_len(ba->read(buffer, sizeof(buffer))); + ensure("Read returns empty", 0 == read_len); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ + set_test_name("BufferArray single write"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + char buffer[256]; + + size_t len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", strlen(str1) == len); + ensure("Recorded size correct", strlen(str1) == ba->size()); + + // read some data back + len = ba->seek(2); + ensure("Seek worked", 2 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 2); + ensure("Read length correct", 2 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read didn't overwrite", 'X' == buffer[2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ + set_test_name("BufferArray multiple writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + len = ba->seek(8); + ensure("Seek worked", 8 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read whole thing + len = ba->seek(0); + ensure("Seek worked (2)", 0 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct", (2 * str1_len) == len); + ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); + ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ + set_test_name("BufferArray overwriting"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJ"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->seek(8); + ensure("Seek worked", 8 == len); + len = ba->write(str2, 4); + ensure("Overwrite length correct", 4 == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // reposition and check + len = ba->seek(6); + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 8); + ensure("Read length correct.2", 8 == len); + ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); + ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); + ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); + ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); + ensure("Read didn't overwrite.7", 'X' == buffer[8]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ + set_test_name("BufferArray multiple writes - sequential reads"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + len = ba->seek(8); + ensure("Seek worked", 8 == len); + + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read some more without repositioning + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct", (str1_len - 2) == len); + ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ + set_test_name("BufferArray overwrite spanning blocks and appending"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->seek(8); + ensure("Seek worked", 8 == len); + len = ba->write(str2, str2_len); + ensure("Overwrite length correct", str2_len == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, 0); + ensure("Read length correct", 0 == len); + ensure("Read didn't overwrite", 'X' == buffer[0]); + + // reposition and check + len = ba->seek(0); + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(buffer, sizeof(buffer)); + ensure("Read length correct.2", (str1_len + str2_len - 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); + ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ + set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(str1, str1_len); + len = ba->write(str1, strlen(str1)); + + // reposition and overwrite + len = ba->seek(6); + len = ba->write(str2, 2); + ensure("Overwrite length correct", 2 == len); + + len = ba->write(str2, 2); + ensure("Overwrite length correct.2", 2 == len); + + len = ba->write(str2, 2); + ensure("Overwrite length correct.3", 2 == len); + + // append some data + len = ba->append(str2, str2_len); + ensure("Append length correct", str2_len == len); + + // append some more + char * out_buf(ba->appendBufferAlloc(str1_len)); + memcpy(out_buf, str1, str1_len); + + // And some final writes + len = ba->write(str2, 2); + ensure("Write length correct.2", 2 == len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + ba->seek(0); + len = ba->read(buffer, sizeof(buffer)); + ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); + ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); + ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); + ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); + ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); + ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); + ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ + set_test_name("BufferArray zero-length appendBufferAlloc"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(str1, str1_len); + len = ba->write(str1, strlen(str1)); + + // zero-length allocate (we allow this with a valid pointer returned) + char * out_buf(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + + // Do it again + char * out_buf2(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); + ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + + // And some final writes + len = ba->write(str2, str2_len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + ba->seek(0); + len = ba->read(buffer, sizeof(buffer)); + ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); + ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); + ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100644 index 0000000000..d22b516691 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/** + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in headers", 0 == headers->mHeaders.size()); + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + + { + // Append a few strings + std::string str1("Pragma:"); + headers->mHeaders.push_back(str1); + std::string str2("Accept: application/json"); + headers->mHeaders.push_back(str2); + + ensure("Headers retained", 2 == headers->mHeaders.size()); + ensure("First is first", headers->mHeaders[0] == str1); + ensure("Second is second", headers->mHeaders[1] == str2); + } + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100644 index 0000000000..c9feaddb2a --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,128 @@ +/** + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: + virtual void onCompleted(HttpHandle, HttpResponse *) + { + std::cout << "TestHandler::onCompleted() invoked" << std::endl; + } + +}; + + +} // end namespace anonymous + + +namespace tut +{ + struct HttpOperationTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group HttpOperationTestGroupType; + typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; + HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + + template <> template <> + void HttpOperationTestObjectType::test<1>() + { + set_test_name("HttpOpNull construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + ensure(op->getRefCount() == 1); + ensure(mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + op->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void HttpOperationTestObjectType::test<2>() + { + set_test_name("HttpOpNull construction with handlers"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get some handlers + TestHandler * h1 = new TestHandler(); + TestHandler * h2 = new TestHandler(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + + // Add the handlers + op->setHandlers(NULL, h1, h2); + + // Check ref count + ensure(op->getRefCount() == 1); + + // release the reference, releasing the operation but + // not the handlers. + op->release(); + op = NULL; + ensure(mMemTotal != GetMemTotal()); + + // release the handlers + delete h1; + h1 = NULL; + delete h2; + h2 = NULL; + + ensure(mMemTotal == GetMemTotal()); + } + +} + +#endif // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100644 index 0000000000..df5640859f --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,437 @@ +/** + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include +#include +#include +#include +#include + +#include + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + int mHandlerCalls; + HttpStatus mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: + TestHandler2(HttpRequestTestData * state, + const std::string & name) + : mState(state), + mName(name), + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + {} + + virtual void onCompleted(HttpHandle handle, HttpResponse * response) + { + if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) + { + ensure("Expected handle received in handler", mExpectHandle == handle); + } + ensure("Handler got a response", NULL != response); + if (response && mState) + { + const HttpStatus actual_status(response->getStatus()); + + ensure("Expected HttpStatus received in response", actual_status == mState->mStatus); + } + if (mState) + { + mState->mHandlerCalls++; + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; + } + + HttpRequestTestData * mState; + std::string mName; + HttpHandle mExpectHandle; +}; + +typedef test_group HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ + set_test_name("HttpRequest construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); + + // release the request object + delete req; + req = NULL; + + HttpRequest::destroyService(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ + set_test_name("HttpRequest and Null Op queued"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + + // release the request object + delete req; + req = NULL; + + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure(mMemTotal < GetMemTotal()); + + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ + set_test_name("HttpRequest NoOp + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ + set_test_name("2 HttpRequest instances, one thread"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + TestHandler2 handler1(this, "handler1"); + TestHandler2 handler2(this, "handler2"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req1 = new HttpRequest(); + HttpRequest * req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ + set_test_name("HttpRequest GET + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + HttpRequest * req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0.0f, + "http://localhost:2/nothing/here", + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // We have left over state so can't really say something + // definitive about memory usage at the end of this. + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ + Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100644 index 0000000000..1de2d8f9ab --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/** + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ + set_test_name("HttpRequestQueue construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ + set_test_name("HttpRequestQueue refcount works"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + rq->addRef(); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + ensure("One ref after term() called", rq->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Drop ref + rq->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ + set_test_name("HttpRequestQueue addOp/fetchOp work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + + rq->addOp(op); // transfer my refcount + + op = rq->fetchOp(true); // Potentially hangs the test on failure + ensure("One goes in, one comes out", NULL != op); + op->release(); + + op = rq->fetchOp(false); + ensure("Better not be two of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ + set_test_name("HttpRequestQueue addOp/fetchAll work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + { + HttpRequestQueue::OpContainer ops; + rq->fetchAll(true, ops); // Potentially hangs the test on failure + ensure("Three go in, three come out", 3 == ops.size()); + + op = rq->fetchOp(false); + ensure("Better not be any more of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // We're still holding onto the ops. + ensure(mMemTotal < GetMemTotal()); + + // Release them + while (! ops.empty()) + { + HttpOperation * op = ops.front(); + ops.erase(ops.begin()); + op->release(); + } + } + + // Should be clean + ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100644 index 0000000000..fa5803a17c --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,163 @@ +/** + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include + +#include +#include + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ + HttpStatusTestData() + {} +}; + +typedef test_group HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ + set_test_name("HttpStatus construction"); + + // auto allocation fine for this + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SUCCESS; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -1; + + ensure(false == bool(status)); + ensure(!(status)); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + + ensure(false == bool(status)); + ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ + set_test_name("HttpStatus memory structure"); + + // Require that an HttpStatus object can be trivially + // returned as a function return value in registers. + // One should fit in an int on all platforms. + + ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ + set_test_name("HttpStatus valid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(msg.empty()); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = CURLM_OUT_OF_MEMORY; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SHUTTING_DOWN; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ + set_test_name("HttpStatus invalid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 32726; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -470; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = 923; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + +} // end namespace tut + +#endif // TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100644 index 0000000000..6a17947288 --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/** + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include <_refcounted.h> + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ + struct RefCountedTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group RefCountedTestGroupType; + typedef RefCountedTestGroupType::object RefCountedTestObjectType; + RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + + template <> template <> + void RefCountedTestObjectType::test<1>() + { + set_test_name("RefCounted construction with implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(true); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<2>() + { + set_test_name("RefCounted construction without implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(false); + ensure(rc->getRefCount() == 0); + + // add a reference + rc->addRef(); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<3>() + { + set_test_name("RefCounted addRef and release"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + for (int i = 0; i < 1024; ++i) + { + rc->addRef(); + } + + ensure(rc->getRefCount() == 1024); + + for (int i = 0; i < 1024; ++i) + { + rc->release(); + } + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<4>() + { + set_test_name("RefCounted isLastRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(true); + + // with only one reference, isLastRef should be true + ensure(rc->isLastRef()); + + // release it to clean up memory + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<5>() + { + set_test_name("RefCounted noRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + // set the noRef + rc->noRef(); + + // with only one reference, isLastRef should be true + ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + + // allow this memory leak, but check that we're leaking a known amount + ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); + } +} + +#endif // TEST_LLCOREINT_REF_COUNTED_H_ -- cgit v1.2.3 From c09669da921f24e8e2693d8652fc56465d5fa19c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 23 Apr 2012 16:20:45 -0400 Subject: And forgot the .cmake library file. --- indra/cmake/LLCoreHttp.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 indra/cmake/LLCoreHttp.cmake diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake new file mode 100644 index 0000000000..280189da3d --- /dev/null +++ b/indra/cmake/LLCoreHttp.cmake @@ -0,0 +1,14 @@ +# -*- cmake -*- + +include(CARes) +include(CURL) +include(OpenSSL) + +set(LLCOREHTTP_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llcorehttp + ${CARES_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIRS} + ) + +set(LLCOREHTTP_LIBRARIES llcorehttp) -- cgit v1.2.3 From 88bc54192fa274bb1617801e4aeddbbf4e9e7944 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 13:18:27 -0400 Subject: Get Mac building. Unused variable in boost and missing return value which wasn't caught in other environments. --- indra/llcorehttp/CMakeLists.txt | 5 +++++ indra/llcorehttp/_thread.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 8d21007ca0..b3f2d8234c 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -58,6 +58,11 @@ set(llcorehttp_HEADER_FILES set_source_files_properties(${llcorehttp_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) +if (APPLE) + # Boost headers define unused members so... + set_source_files_properties(${llcorehttp_SOURCE_FILES} + PROPERTIES COMPILE_FLAGS -Wno-unused-variable) +endif (APPLE) list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 5960f0dcdb..0937d698c7 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -91,7 +91,7 @@ public: inline bool joinable() const { - mThread->joinable(); + return mThread->joinable(); } private: -- cgit v1.2.3 From a33bec1e5ef5e399f41267f06dc6d355dd34efe6 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 14:43:31 -0400 Subject: Build tweak for linux which has the same boost/unused variable problem as Mac. --- indra/llcorehttp/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index b3f2d8234c..e0d1817781 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -58,11 +58,11 @@ set(llcorehttp_HEADER_FILES set_source_files_properties(${llcorehttp_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -if (APPLE) - # Boost headers define unused members so... +if (DARWIN OR LINUX) + # Boost headers define unused members in condition_variable so... set_source_files_properties(${llcorehttp_SOURCE_FILES} PROPERTIES COMPILE_FLAGS -Wno-unused-variable) -endif (APPLE) +endif (DARWIN OR LINUX) list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) -- cgit v1.2.3 From a54e9c379550f7901404990ea7fddb99c0188048 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 25 Apr 2012 15:47:30 -0400 Subject: Another fix for Mac warnings. Uninitialized auto check. Not an actual problem but this will quiet the compiler. --- indra/llcorehttp/_httpoprequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index d71ace5d57..316df8bd49 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -410,7 +410,7 @@ int parse_content_range_header(char * buffer, unsigned int * last, unsigned int * length) { - char * tok_state(NULL), * tok; + char * tok_state(NULL), * tok(NULL); bool match(true); if (! strtok_r(buffer, ": \t", &tok_state)) -- cgit v1.2.3 From 438a6431e418eac5a3a4e00f7adfe379994869d7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 26 Apr 2012 11:35:07 -0400 Subject: Bring llcorehttp into the compile and link phases. Windows looks okay though it's a dead library so far. --- indra/llcorehttp/httpcommon.h | 14 +++++++------- indra/newview/CMakeLists.txt | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 617286fb38..cd7c09f097 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -44,13 +44,13 @@ /// - Minimal data sharing across threads for correctness and low latency. /// /// The public interface is declared in a few key header files: -/// - -/// - -/// - -/// - -/// - -/// - -/// - +/// - "llcorehttp/bufferarray.h" +/// - "llcorehttp/httpcommon.h" +/// - "llcorehttp/httphandler.h" +/// - "llcorehttp/httpheaders.h" +/// - "llcorehttp/httpoptions.h" +/// - "llcorehttp/httprequest.h" +/// - "llcorehttp/httpresponse.h" /// /// The library is still under early development and particular users /// may need access to internal implementation details that are found diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f85b943c70..fc9d89aea0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(LLAudio) include(LLCharacter) include(LLCommon) include(LLConvexDecomposition) +include(LLCoreHttp) include(LLImage) include(LLImageJ2COJ) include(LLInventory) @@ -52,6 +53,7 @@ include_directories( ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCONVEXDECOMP_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLIMAGE_INCLUDE_DIRS} ${LLKDU_INCLUDE_DIRS} @@ -1747,6 +1749,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLXUIXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${viewer_LIBRARIES} -- cgit v1.2.3 From efd19dddd93117688afe7de24c890c5a4b30bada Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 2 May 2012 10:37:51 -0700 Subject: SH-3126 : Improve curl byte range call, test implementation in lltexturefetch (not final) --- indra/llmessage/llcurl.cpp | 9 ++++++++- indra/newview/lltexturefetch.cpp | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 4d92cfd32f..095d8cd28b 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -1074,7 +1074,9 @@ void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) { getByteRange(url, headers_t(), 0, -1, responder); } - + +// Note: (length==0) is interpreted as "the rest of the file", i.e. the whole file if (offset==0) or +// the remainder of the file if not. bool LLCurlRequest::getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, @@ -1092,6 +1094,11 @@ bool LLCurlRequest::getByteRange(const std::string& url, std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1); easy->slist_append(range.c_str()); } + else if (offset > 0) + { + std::string range = llformat("Range: bytes=%d-", offset); + easy->slist_append(range.c_str()); + } easy->setHeaders(); bool res = addEasy(easy); return res; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2e1b409fa7..6afecd7e13 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -775,6 +775,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() } // mWorkMutex is locked +// Merov : Change so to take into account size == 0 == max void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -1214,6 +1215,11 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes std::vector headers; headers.push_back("Accept: image/x-j2c"); + if (mRequestedSize == MAX_IMAGE_DATA_SIZE) + { + mRequestedSize = 0; + llinfos << "Merov debug : getByteRange, offset = " << offset << ", id = " << mID << llendl; + } res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); } -- cgit v1.2.3 From b679975d8fdc22d33d0f62288934be8f0209157e Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 2 May 2012 15:52:40 -0700 Subject: SH-3126 : Use correct unspecified ending byte range for all curl request when fetching files to the end. --- indra/newview/lltexturefetch.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6afecd7e13..bcebac712c 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1215,10 +1215,11 @@ bool LLTextureFetchWorker::doWork(S32 param) // Will call callbackHttpGet when curl request completes std::vector headers; headers.push_back("Accept: image/x-j2c"); - if (mRequestedSize == MAX_IMAGE_DATA_SIZE) + // If we try to fetch the whole file, we set the size to 0 so that we generate the correct curl range request + // Note: it looks a bit hacky but we need to limit this (size==0) to mean "whole file" to HTTP only as it messes up UDP fetching + if ((offset+mRequestedSize) == MAX_IMAGE_DATA_SIZE) { mRequestedSize = 0; - llinfos << "Merov debug : getByteRange, offset = " << offset << ", id = " << mID << llendl; } res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); @@ -1701,7 +1702,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); buffer->readAfter(channels.in(), NULL, mBuffer, data_size); mBufferSize += data_size; - if (data_size < mRequestedSize && mRequestedDiscard == 0) + if ((data_size < mRequestedSize) || (mRequestedSize == 0)) { mHaveAllData = TRUE; } @@ -1920,6 +1921,8 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con } else { + // If the requester knows nothing about the file, we fetch the smallest + // amount of data at the lowest resolution (highest discard level) possible. desired_size = TEXTURE_CACHE_ENTRY_SIZE; desired_discard = MAX_DISCARD_LEVEL; } -- cgit v1.2.3 From e65852e9a58ec92763b88369f53092628ab7a6b8 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 3 May 2012 11:38:05 -0600 Subject: For SH-3086: add functions to refetch all previous textures through HTTP and from cache --- indra/newview/app_settings/settings.xml | 11 ++ indra/newview/llfloatertexturefetchdebugger.cpp | 91 +++++++++-- indra/newview/llfloatertexturefetchdebugger.h | 2 + indra/newview/lltexturefetch.cpp | 180 +++++++++++++++++++-- indra/newview/lltexturefetch.h | 40 ++++- indra/newview/llviewertexture.cpp | 16 ++ indra/newview/llviewertexture.h | 3 + indra/newview/llviewertexturelist.cpp | 26 +++ indra/newview/llviewertexturelist.h | 1 + .../xui/en/floater_texture_fetch_debugger.xml | 109 ++++++++++++- 10 files changed, 442 insertions(+), 37 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c2f3d244f3..88c5831ce7 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10708,6 +10708,17 @@ Value 0 + TextureFetchSource + + Comment + Debug use: Source to fetch textures + Persist + 1 + Type + S32 + Value + 0 + TextureLoadFullRes Comment diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp index 2b34b72055..b6210423d1 100644 --- a/indra/newview/llfloatertexturefetchdebugger.cpp +++ b/indra/newview/llfloatertexturefetchdebugger.cpp @@ -59,6 +59,8 @@ LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key) mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisCache", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisCache, this)); mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisHTTP", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP, this)); + mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchAllCache", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchAllCache, this)); + mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchAllHTTP", boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchAllHTTP, this)); } //---------------------------------------------- @@ -76,8 +78,10 @@ BOOL LLFloaterTextureFetchDebugger::postBuild(void) mButtonStateMap["decode_btn"] = false; mButtonStateMap["gl_btn"] = false; - mButtonStateMap["refetchviscache_btn"] = true; - mButtonStateMap["refetchvishttp_btn"] = true; + mButtonStateMap["refetchviscache_btn"] = false; + mButtonStateMap["refetchvishttp_btn"] = false; + mButtonStateMap["refetchallcache_btn"] = false; + mButtonStateMap["refetchallhttp_btn"] = false; updateButtons(); @@ -118,6 +122,8 @@ void LLFloaterTextureFetchDebugger::disableButtons() childDisable("gl_btn"); childDisable("refetchviscache_btn"); childDisable("refetchvishttp_btn"); + childDisable("refetchallcache_btn"); + childDisable("refetchallhttp_btn"); } void LLFloaterTextureFetchDebugger::idle() @@ -130,8 +136,7 @@ void LLFloaterTextureFetchDebugger::idle() { case LLTextureFetchDebugger::IDLE: break; - case LLTextureFetchDebugger::READ_CACHE: - mButtonStateMap["cachewrite_btn"] = true; + case LLTextureFetchDebugger::READ_CACHE: mButtonStateMap["decode_btn"] = true; updateButtons(); break; @@ -156,6 +161,11 @@ void LLFloaterTextureFetchDebugger::idle() case LLTextureFetchDebugger::REFETCH_VIS_HTTP: updateButtons(); break; + case LLTextureFetchDebugger::REFETCH_ALL_CACHE: + updateButtons(); + case LLTextureFetchDebugger::REFETCH_ALL_HTTP: + updateButtons(); + break; default: break; } @@ -175,8 +185,27 @@ void LLFloaterTextureFetchDebugger::onClickStart() mDebugger->startDebug(); mButtonStateMap["start_btn"] = false; - mButtonStateMap["cacheread_btn"] = true; - mButtonStateMap["http_btn"] = true; + + if(LLAppViewer::getTextureFetch()->canLoadFromCache()) + { + mButtonStateMap["cacheread_btn"] = true; + mButtonStateMap["http_btn"] = false; + mButtonStateMap["refetchviscache_btn"] = true; + mButtonStateMap["refetchvishttp_btn"] = false; + mButtonStateMap["refetchallcache_btn"] = true; + mButtonStateMap["refetchallhttp_btn"] = false; + mButtonStateMap["cachewrite_btn"] = false; + } + else + { + mButtonStateMap["cacheread_btn"] = true; + mButtonStateMap["http_btn"] = true; + mButtonStateMap["refetchviscache_btn"] = true; + mButtonStateMap["refetchvishttp_btn"] = true; + mButtonStateMap["refetchallcache_btn"] = true; + mButtonStateMap["refetchallhttp_btn"] = true; + } + updateButtons(); } @@ -256,6 +285,20 @@ void LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP() mDebugger->debugRefetchVisibleFromHTTP(); } +void LLFloaterTextureFetchDebugger::onClickRefetchAllCache() +{ + disableButtons(); + + mDebugger->debugRefetchAllFromCache(); +} + +void LLFloaterTextureFetchDebugger::onClickRefetchAllHTTP() +{ + disableButtons(); + + mDebugger->debugRefetchAllFromHTTP(); +} + void LLFloaterTextureFetchDebugger::draw() { //total number of fetched textures @@ -368,8 +411,22 @@ void LLFloaterTextureFetchDebugger::draw() else { getChild("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisCacheTime())); - getChild("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10)); - getChild("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f)); + getChild("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedVisData() >> 10)); + getChild("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedVisPixels() / 1000000.f)); + } + + //total time on refetching all textures from cache + if(mDebugger->getRefetchAllCacheTime() < 0.f) + { + getChild("total_time_refetch_all_cache_label")->setTextArg("[TIME]", std::string("----")); + getChild("total_time_refetch_all_cache_label")->setTextArg("[SIZE]", std::string("----")); + getChild("total_time_refetch_all_cache_label")->setTextArg("[PIXEL]", std::string("----")); + } + else + { + getChild("total_time_refetch_all_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchAllCacheTime())); + getChild("total_time_refetch_all_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedAllData() >> 10)); + getChild("total_time_refetch_all_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedAllPixels() / 1000000.f)); } //total time on refetching visible textures from http @@ -382,8 +439,22 @@ void LLFloaterTextureFetchDebugger::draw() else { getChild("total_time_refetch_vis_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisHTTPTime())); - getChild("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10)); - getChild("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f)); + getChild("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedVisData() >> 10)); + getChild("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedVisPixels() / 1000000.f)); + } + + //total time on refetching all textures from http + if(mDebugger->getRefetchAllHTTPTime() < 0.f) + { + getChild("total_time_refetch_all_http_label")->setTextArg("[TIME]", std::string("----")); + getChild("total_time_refetch_all_http_label")->setTextArg("[SIZE]", std::string("----")); + getChild("total_time_refetch_all_http_label")->setTextArg("[PIXEL]", std::string("----")); + } + else + { + getChild("total_time_refetch_all_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchAllHTTPTime())); + getChild("total_time_refetch_all_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedAllData() >> 10)); + getChild("total_time_refetch_all_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedAllPixels() / 1000000.f)); } LLFloater::draw(); diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h index 33012c6a3d..da1bff19d5 100644 --- a/indra/newview/llfloatertexturefetchdebugger.h +++ b/indra/newview/llfloatertexturefetchdebugger.h @@ -53,6 +53,8 @@ public: void onClickRefetchVisCache(); void onClickRefetchVisHTTP(); + void onClickRefetchAllCache(); + void onClickRefetchAllHTTP(); public: void idle() ; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index efb6ed6079..5e2141caf6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -944,7 +944,7 @@ bool LLTextureFetchWorker::doWork(S32 param) offset, size, responder); mCacheReadTimer.reset(); } - else if (mUrl.empty()) + else if (mUrl.empty() && mFetcher->canLoadFromCache()) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it @@ -953,7 +953,7 @@ bool LLTextureFetchWorker::doWork(S32 param) offset, size, responder); mCacheReadTimer.reset(); } - else if(mCanUseHTTP) + else if(!mUrl.empty() && mCanUseHTTP && mFetcher->canLoadFromNetwork()) { if (!(mUrl.compare(0, 7, "http://") == 0)) { @@ -963,11 +963,15 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = SEND_HTTP_REQ; } - else + else if(mFetcher->canLoadFromNetwork()) { setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = LOAD_FROM_NETWORK; } + else + { + return true; //failed + } } if (mLoaded) @@ -1015,11 +1019,15 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; } // need more data - else + else if(mFetcher->canLoadFromNetwork()) { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; mState = LOAD_FROM_NETWORK; } + else + { + return true; //failed + } // fall through LLTextureFetch::sCacheHitRate.addValue(0.f); } @@ -1060,7 +1068,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = LLTextureFetchWorker::SEND_HTTP_REQ; + mState = SEND_HTTP_REQ; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1860,7 +1868,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTotalHTTPRequests(0), mCurlGetRequest(NULL), mQAMode(qa_mode), - mFetchDebugger(NULL) + mFetchDebugger(NULL), + mFetchSource(LLTextureFetch::FROM_ALL), + mOriginFetchSource(LLTextureFetch::FROM_ALL) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); @@ -1870,6 +1880,13 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image if(LLTextureFetchDebugger::isEnabled()) { mFetchDebugger = new LLTextureFetchDebugger(this, cache, imagedecodethread) ; + mFetchSource = (e_tex_source)gSavedSettings.getS32("TextureFetchSource"); + if(mFetchSource < 0 && mFetchSource >= INVALID_SOURCE) + { + mFetchSource = LLTextureFetch::FROM_ALL; + gSavedSettings.setS32("TextureFetchSource", 0); + } + mOriginFetchSource = mFetchSource; } } @@ -3190,6 +3207,8 @@ void LLTextureFetchDebugger::init() mTotalFetchingTime = 0.f; mRefetchVisCacheTime = -1.f; mRefetchVisHTTPTime = -1.f; + mRefetchAllCacheTime = -1.f; + mRefetchAllHTTPTime = -1.f; mNumFetchedTextures = 0; mNumCacheHits = 0; @@ -3203,8 +3222,10 @@ void LLTextureFetchDebugger::init() mRenderedDecodedData = 0; mFetchedPixels = 0; mRenderedPixels = 0; - mRefetchedData = 0; - mRefetchedPixels = 0; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; mFreezeHistory = FALSE; } @@ -3313,8 +3334,10 @@ void LLTextureFetchDebugger::stopDebug() //unlock the fetcher mFetcher->lockFetcher(false); + mFetcher->resetLoadSource(); mFreezeHistory = FALSE; mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset + mRefetchList.clear(); } //called in the main thread and when the fetching queue is empty @@ -3328,8 +3351,25 @@ void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker) { if(mFreezeHistory) { - mRefetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); - mRefetchedData += worker->mFormattedImage->getDataSize(); + if(mState == REFETCH_VIS_CACHE || mState == REFETCH_VIS_HTTP) + { + mRefetchedVisPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); + mRefetchedVisData += worker->mFormattedImage->getDataSize(); + } + else + { + mRefetchedAllPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); + mRefetchedAllData += worker->mFormattedImage->getDataSize(); + + LLViewerFetchedTexture* tex = LLViewerTextureManager::findFetchedTexture(worker->mID); + if(tex && mRefetchList[tex].begin() != mRefetchList[tex].end()) + { + if(worker->mDecodedDiscard == mFetchingHistory[mRefetchList[tex][0]].mDecodedLevel) + { + mRefetchList[tex].erase(mRefetchList[tex].begin()); + } + } + } return; } @@ -3341,9 +3381,8 @@ void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker) mDecodedData += worker->mRawImage->getDataSize(); mFetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight(); - mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); - //mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mHaveAllData ? 0 : worker->mLoadedDiscard, worker->mFormattedImage->getComponents(), - //worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); + mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, + worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize())); } void LLTextureFetchDebugger::lockCache() @@ -3360,6 +3399,7 @@ void LLTextureFetchDebugger::debugCacheRead() llassert_always(mState == IDLE); mTimer.reset(); mState = READ_CACHE; + mCacheReadTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3395,6 +3435,7 @@ void LLTextureFetchDebugger::debugCacheWrite() llassert_always(mState == IDLE); mTimer.reset(); mState = WRITE_CACHE; + mCacheWriteTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3423,6 +3464,7 @@ void LLTextureFetchDebugger::debugDecoder() llassert_always(mState == IDLE); mTimer.reset(); mState = DECODING; + mDecodingTime = -1.f; S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3458,6 +3500,7 @@ void LLTextureFetchDebugger::debugHTTP() mTimer.reset(); mState = HTTP_FETCHING; + mHTTPTime = -1.f; S32 size = mFetchingHistory.size(); for (S32 i = 0 ; i < size ; i++) @@ -3529,6 +3572,8 @@ void LLTextureFetchDebugger::debugGLTextureCreation() } mTimer.reset(); + mGLCreationTime = -1.f; + S32 j = 0 ; S32 size1 = tex_list.size(); for(S32 i = 0 ; i < size && j < size1; i++) @@ -3561,15 +3606,54 @@ void LLTextureFetchDebugger::clearTextures() } } +void LLTextureFetchDebugger::makeRefetchList() +{ + mRefetchList.clear(); + S32 size = mFetchingHistory.size(); + for(S32 i = 0 ; i < size; i++) + { + LLViewerFetchedTexture* tex = LLViewerTextureManager::getFetchedTexture(mFetchingHistory[i].mID); + if(tex && tex->isJustBound()) //visible + { + continue; //the texture fetch pipeline will take care of visible textures. + } + + mRefetchList[tex].push_back(i); + } +} + +void LLTextureFetchDebugger::scanRefetchList() +{ + for(std::map< LLPointer, std::vector >::iterator iter = mRefetchList.begin(); + iter != mRefetchList.end(); ) + { + if(iter->second.empty()) + { + gTextureList.setDebugFetching(iter->first, -1); + iter = mRefetchList.erase(iter); + } + else + { + gTextureList.setDebugFetching(iter->first, mFetchingHistory[iter->second[0]].mDecodedLevel); + ++iter; + } + } +} + void LLTextureFetchDebugger::debugRefetchVisibleFromCache() { llassert_always(mState == IDLE); mState = REFETCH_VIS_CACHE; clearTextures(); + mFetcher->setLoadSource(LLTextureFetch::FROM_CACHE_ONLY); mTimer.reset(); mFetcher->lockFetcher(false); + mRefetchVisCacheTime = -1.f; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; + mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); } void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() @@ -3579,9 +3663,49 @@ void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() clearCache(); clearTextures(); + mFetcher->setLoadSource(LLTextureFetch::FROM_NETWORK_ONLY); + + mTimer.reset(); + mFetcher->lockFetcher(false); + mRefetchVisHTTPTime = -1.f; + mRefetchedVisData = 0; + mRefetchedVisPixels = 0; + mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); +} + +void LLTextureFetchDebugger::debugRefetchAllFromCache() +{ + llassert_always(mState == IDLE); + mState = REFETCH_ALL_CACHE; + + clearTextures(); + makeRefetchList(); + mFetcher->setLoadSource(LLTextureFetch::FROM_CACHE_ONLY); + + mTimer.reset(); + mFetcher->lockFetcher(false); + mRefetchAllCacheTime = -1.f; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; + mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); +} + +void LLTextureFetchDebugger::debugRefetchAllFromHTTP() +{ + llassert_always(mState == IDLE); + mState = REFETCH_ALL_HTTP; + + clearCache(); + clearTextures(); + makeRefetchList(); + mFetcher->setLoadSource(LLTextureFetch::FROM_NETWORK_ONLY); mTimer.reset(); mFetcher->lockFetcher(false); + mRefetchAllHTTPTime = -1.f; + mRefetchedAllData = 0; + mRefetchedAllPixels = 0; + mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); } bool LLTextureFetchDebugger::update() @@ -3627,17 +3751,41 @@ bool LLTextureFetchDebugger::update() case REFETCH_VIS_CACHE: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; mState = IDLE; mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); } break; case REFETCH_VIS_HTTP: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; + mState = IDLE; + mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + } + break; + case REFETCH_ALL_CACHE: + scanRefetchList(); + if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) + { + mRefetchAllCacheTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; + mState = IDLE; + mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + mRefetchList.clear(); + } + break; + case REFETCH_ALL_HTTP: + scanRefetchList(); + if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) + { + mRefetchAllHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; mState = IDLE; mFetcher->lockFetcher(true); + mFetcher->resetLoadSource(); + mRefetchList.clear(); } break; default: @@ -3692,7 +3840,7 @@ void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& ch U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); buffer->readAfter(channels.in(), NULL, d_buffer, data_size); - llassert_always(mFetchingHistory[id].mFormattedImage.isNull()); + mFetchingHistory[id].mFormattedImage = NULL; { // For now, create formatted image based on extension std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str(); diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 107e1623b0..ff78d34eda 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -216,18 +216,36 @@ public: // reporting due to either startup or a problem POSTing data. static volatile bool svMetricsDataBreak; +public: + //debug use + enum e_tex_source + { + FROM_ALL = 0, + FROM_CACHE_ONLY, + FROM_NETWORK_ONLY, + INVALID_SOURCE + }; private: //debug use LLTextureFetchDebugger* mFetchDebugger; bool mFetcherLocked; + + e_tex_source mFetchSource; + e_tex_source mOriginFetchSource; public: //debug use LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;} void lockFetcher(bool lock) { mFetcherLocked = lock;} + + void setLoadSource(e_tex_source source) {mFetchSource = source;} + void resetLoadSource() {mFetchSource = mOriginFetchSource;} + bool canLoadFromCache() { return mFetchSource != FROM_NETWORK_ONLY;} + bool canLoadFromNetwork() { return mFetchSource != FROM_CACHE_ONLY;} }; //debug use +class LLViewerFetchedTexture; class LLTextureFetchDebugger { friend class LLTextureFetch; @@ -301,6 +319,8 @@ private: F32 mTotalFetchingTime; F32 mRefetchVisCacheTime; F32 mRefetchVisHTTPTime; + F32 mRefetchAllCacheTime; + F32 mRefetchAllHTTPTime; LLTimer mTimer; @@ -321,8 +341,10 @@ private: U32 mRenderedDecodedData; U32 mFetchedPixels; U32 mRenderedPixels; - U32 mRefetchedData; - U32 mRefetchedPixels; + U32 mRefetchedVisData; + U32 mRefetchedVisPixels; + U32 mRefetchedAllData; + U32 mRefetchedAllPixels; BOOL mFreezeHistory; @@ -330,6 +352,8 @@ private: S32 mNbCurlRequests; S32 mNbCurlCompleted; + std::map< LLPointer, std::vector > mRefetchList; + F32 mRefetchStartTime; public: bool update(); //called in the main thread once per frame @@ -348,6 +372,8 @@ public: void debugGLTextureCreation(); void debugRefetchVisibleFromCache(); void debugRefetchVisibleFromHTTP(); + void debugRefetchAllFromCache(); + void debugRefetchAllFromHTTP(); void callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); @@ -372,8 +398,10 @@ public: U32 getRenderedDecodedData() {return mRenderedDecodedData;} U32 getFetchedPixels() {return mFetchedPixels;} U32 getRenderedPixels() {return mRenderedPixels;} - U32 getRefetchedData() {return mRefetchedData;} - U32 getRefetchedPixels() {return mRefetchedPixels;} + U32 getRefetchedVisData() {return mRefetchedVisData;} + U32 getRefetchedVisPixels() {return mRefetchedVisPixels;} + U32 getRefetchedAllData() {return mRefetchedAllData;} + U32 getRefetchedAllPixels() {return mRefetchedAllPixels;} F32 getCacheReadTime() {return mCacheReadTime;} F32 getCacheWriteTime() {return mCacheWriteTime;} @@ -383,11 +411,15 @@ public: F32 getTotalFetchingTime() {return mTotalFetchingTime;} F32 getRefetchVisCacheTime() {return mRefetchVisCacheTime;} F32 getRefetchVisHTTPTime() {return mRefetchVisHTTPTime;} + F32 getRefetchAllCacheTime() {return mRefetchAllCacheTime;} + F32 getRefetchAllHTTPTime() {return mRefetchAllHTTPTime;} private: void init(); void clearTextures();//clear fetching results of all textures. void clearCache(); + void makeRefetchList(); + void scanRefetchList(); void lockFetcher(); void unlockFetcher(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index ea329f6aac..684d1afa4f 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1266,6 +1266,8 @@ void LLViewerFetchedTexture::init(bool firstinit) mLastReferencedSavedRawImageTime = 0.0f ; mKeptSavedRawImageTime = 0.f ; mLastCallBackActiveTime = 0.f; + + mInDebug = FALSE; } LLViewerFetchedTexture::~LLViewerFetchedTexture() @@ -1898,6 +1900,20 @@ S32 LLViewerFetchedTexture::getCurrentDiscardLevelForFetching() return current_discard ; } +bool LLViewerFetchedTexture::setDebugFetching(S32 debug_level) +{ + if(debug_level < 0) + { + mInDebug = FALSE; + return false; + } + mInDebug = TRUE; + + mDesiredDiscardLevel = debug_level; + + return true; +} + bool LLViewerFetchedTexture::updateFetch() { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 41bf625225..c1ebbd9ebe 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -433,6 +433,8 @@ public: void setMinDiscardLevel(S32 discard) { mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel,(S8)discard); } bool updateFetch(); + bool setDebugFetching(S32 debug_level); + bool isInDebug() {return mInDebug;} void clearFetchedResults(); //clear all fetched results, for debug use. @@ -513,6 +515,7 @@ private: private: BOOL mFullyLoaded; + BOOL mInDebug; protected: std::string mLocalFileName; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 2008a884db..e3b633dc0e 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -701,6 +701,11 @@ void LLViewerTextureList::updateImagesDecodePriorities() LLPointer imagep = iter->second; ++iter; // safe to incrament now + if(imagep->isInDebug()) + { + continue; //is in debug, ignore. + } + // // Flush formatted images using a lazy flush // @@ -773,6 +778,27 @@ void LLViewerTextureList::updateImagesDecodePriorities() } } +void LLViewerTextureList::setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level) +{ + if(!tex->setDebugFetching(debug_level)) + { + return; + } + + const F32 DEBUG_PRIORITY = 100000.f; + F32 old_priority_test = llmax(tex->getDecodePriority(), 0.0f); + F32 decode_priority_test = DEBUG_PRIORITY; + + // Ignore < 20% difference + if ((decode_priority_test < old_priority_test * .8f) || + (decode_priority_test > old_priority_test * 1.25f)) + { + removeImageFromList(tex); + tex->setDecodePriority(decode_priority_test); + addImageToList(tex); + } +} + /* static U8 get_image_type(LLViewerFetchedTexture* imagep, LLHost target_host) { diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 7038ea24ce..86429fa899 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -110,6 +110,7 @@ public: void doPrefetchImages(); void clearFetchingRequests(); + void setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level); static S32 getMinVideoRamSetting(); static S32 getMaxVideoRamSetting(bool get_recommended = false); diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml index 44b6a63bca..ca2f9bfd94 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml @@ -2,7 +2,7 @@ + 16, Refetching all textures from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + - 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + 17, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + 18, Refetching all textures from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + 20, Texture Source: + + + + + + + + -- cgit v1.2.3 From 30140f66be90b78006b29b7f3740ddc473a0ac96 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Thu, 3 May 2012 17:25:02 -0400 Subject: SH-3079 FIX - minimize retries and avoid log spam when people API is missing --- indra/llmessage/llavatarnamecache.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 97f2792686..fbc3cc6de2 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -87,6 +87,9 @@ namespace LLAvatarNameCache /// Time when unrefreshed cached names were checked last static F64 sLastExpireCheck; + /// Time-to-live for a temp cache entry. + const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; + //----------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------- @@ -274,7 +277,7 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) { // there is no existing cache entry, so make a temporary name from legacy LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " - << agent_id << LL_ENDL; + << agent_id << LL_ENDL; gCacheName->get(agent_id, false, // legacy compatibility boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); @@ -287,13 +290,14 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) // Clear this agent from the pending list LLAvatarNameCache::sPendingQueue.erase(agent_id); - const LLAvatarName& av_name = existing->second; + LLAvatarName& av_name = existing->second; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " << agent_id << "user '" << av_name.mUsername << "' " << "display '" << av_name.mDisplayName << "' " << "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds" << LL_ENDL; + av_name.mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; // reset expiry time so we don't constantly rerequest. } } @@ -402,10 +406,12 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, << LL_ENDL; buildLegacyName(full_name, &av_name); - // Don't add to cache, the data already exists in the legacy name system - // cache and we don't want or need duplicate storage, because keeping the - // two copies in sync is complex. - processName(agent_id, av_name, false); + // Add to cache, because if we don't we'll keep rerequesting the + // same record forever. buildLegacyName should always guarantee + // that these records expire reasonably soon + // (in TEMP_CACHE_ENTRY_LIFETIME seconds), so if the failure was due + // to something temporary we will eventually request and get the right data. + processName(agent_id, av_name, true); } void LLAvatarNameCache::requestNamesViaLegacy() @@ -583,7 +589,7 @@ void LLAvatarNameCache::buildLegacyName(const std::string& full_name, av_name->mDisplayName = full_name; av_name->mIsDisplayNameDefault = true; av_name->mIsTemporaryName = true; - av_name->mExpires = F64_MAX; // not used because these are not cached + av_name->mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName " << full_name << LL_ENDL; -- cgit v1.2.3 From 0afca8603a56205df4c4e310387651726cbfbe3c Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 4 May 2012 18:16:37 -0600 Subject: fix crashes and flaws for SH-3086 --- indra/newview/llfloatertexturefetchdebugger.cpp | 145 +++++++++++++++------ indra/newview/llfloatertexturefetchdebugger.h | 3 + indra/newview/lltexturefetch.cpp | 125 ++++++++++++------ indra/newview/lltexturefetch.h | 14 +- indra/newview/llviewertexture.cpp | 11 +- .../xui/en/floater_texture_fetch_debugger.xml | 14 +- 6 files changed, 208 insertions(+), 104 deletions(-) diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp index b6210423d1..02872f54a0 100644 --- a/indra/newview/llfloatertexturefetchdebugger.cpp +++ b/indra/newview/llfloatertexturefetchdebugger.cpp @@ -67,6 +67,7 @@ LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key) BOOL LLFloaterTextureFetchDebugger::postBuild(void) { mDebugger = LLAppViewer::getTextureFetch()->getFetchDebugger(); + mStartStatus = (S32)LLTextureFetchDebugger::IDLE; //set states for buttons mButtonStateMap["start_btn"] = true; @@ -93,7 +94,7 @@ BOOL LLFloaterTextureFetchDebugger::postBuild(void) LLFloaterTextureFetchDebugger::~LLFloaterTextureFetchDebugger() { //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); } void LLFloaterTextureFetchDebugger::updateButtons() @@ -125,11 +126,69 @@ void LLFloaterTextureFetchDebugger::disableButtons() childDisable("refetchallcache_btn"); childDisable("refetchallhttp_btn"); } +void LLFloaterTextureFetchDebugger::setStartStatus(S32 status) +{ + llassert_always(LLTextureFetchDebugger::IDLE == (LLTextureFetchDebugger::e_debug_state)mStartStatus) ; + mStartStatus = status; +} + +bool LLFloaterTextureFetchDebugger::idleStart() +{ + switch((LLTextureFetchDebugger::e_debug_state)mStartStatus) + { + case LLTextureFetchDebugger::IDLE: + break; + case LLTextureFetchDebugger::START_DEBUG: + mDebugger->startDebug(); + break; + case LLTextureFetchDebugger::READ_CACHE: + mDebugger->debugCacheRead(); + break; + case LLTextureFetchDebugger::WRITE_CACHE: + mDebugger->debugCacheWrite(); + break; + case LLTextureFetchDebugger::DECODING: + mDebugger->debugDecoder(); + break; + case LLTextureFetchDebugger::HTTP_FETCHING: + mDebugger->debugHTTP(); + break; + case LLTextureFetchDebugger::GL_TEX: + mDebugger->debugGLTextureCreation(); + break; + case LLTextureFetchDebugger::REFETCH_VIS_CACHE: + mDebugger->debugRefetchVisibleFromCache(); + break; + case LLTextureFetchDebugger::REFETCH_VIS_HTTP: + mDebugger->debugRefetchVisibleFromHTTP(); + break; + case LLTextureFetchDebugger::REFETCH_ALL_CACHE: + mDebugger->debugRefetchAllFromCache(); + break; + case LLTextureFetchDebugger::REFETCH_ALL_HTTP: + mDebugger->debugRefetchAllFromHTTP(); + break; + default: + break; + } + + if(mStartStatus != (S32)LLTextureFetchDebugger::IDLE) + { + mStartStatus = (S32)LLTextureFetchDebugger::IDLE; + return true; + } + + return false; +} void LLFloaterTextureFetchDebugger::idle() { - LLTextureFetchDebugger::e_debug_state state = mDebugger->getState(); - + if(idleStart()) + { + return; + } + + LLTextureFetchDebugger::e_debug_state state = mDebugger->getState(); if(mDebugger->update()) { switch(state) @@ -137,38 +196,36 @@ void LLFloaterTextureFetchDebugger::idle() case LLTextureFetchDebugger::IDLE: break; case LLTextureFetchDebugger::READ_CACHE: - mButtonStateMap["decode_btn"] = true; - updateButtons(); + mButtonStateMap["decode_btn"] = true; break; - case LLTextureFetchDebugger::WRITE_CACHE: - updateButtons(); + case LLTextureFetchDebugger::WRITE_CACHE: break; case LLTextureFetchDebugger::DECODING: - mButtonStateMap["gl_btn"] = true; - updateButtons(); + mButtonStateMap["gl_btn"] = true; break; case LLTextureFetchDebugger::HTTP_FETCHING: mButtonStateMap["cacheread_btn"] = true; mButtonStateMap["cachewrite_btn"] = true; - mButtonStateMap["decode_btn"] = true; - updateButtons(); + mButtonStateMap["decode_btn"] = true; break; - case LLTextureFetchDebugger::GL_TEX: - updateButtons(); + case LLTextureFetchDebugger::GL_TEX: break; - case LLTextureFetchDebugger::REFETCH_VIS_CACHE: - updateButtons(); - case LLTextureFetchDebugger::REFETCH_VIS_HTTP: - updateButtons(); + case LLTextureFetchDebugger::REFETCH_VIS_CACHE: + break; + case LLTextureFetchDebugger::REFETCH_VIS_HTTP: + break; + case LLTextureFetchDebugger::REFETCH_ALL_CACHE: break; - case LLTextureFetchDebugger::REFETCH_ALL_CACHE: - updateButtons(); case LLTextureFetchDebugger::REFETCH_ALL_HTTP: - updateButtons(); break; default: break; } + + if(state != LLTextureFetchDebugger::IDLE) + { + updateButtons(); + } } } @@ -182,21 +239,21 @@ void LLFloaterTextureFetchDebugger::onClickStart() { disableButtons(); - mDebugger->startDebug(); + setStartStatus((S32)LLTextureFetchDebugger::START_DEBUG); mButtonStateMap["start_btn"] = false; - if(LLAppViewer::getTextureFetch()->canLoadFromCache()) - { - mButtonStateMap["cacheread_btn"] = true; - mButtonStateMap["http_btn"] = false; - mButtonStateMap["refetchviscache_btn"] = true; - mButtonStateMap["refetchvishttp_btn"] = false; - mButtonStateMap["refetchallcache_btn"] = true; - mButtonStateMap["refetchallhttp_btn"] = false; - mButtonStateMap["cachewrite_btn"] = false; - } - else + //if(LLAppViewer::getTextureFetch()->canLoadFromCache()) + //{ + // mButtonStateMap["cacheread_btn"] = true; + // mButtonStateMap["http_btn"] = false; + // mButtonStateMap["refetchviscache_btn"] = true; + // mButtonStateMap["refetchvishttp_btn"] = false; + // mButtonStateMap["refetchallcache_btn"] = true; + // mButtonStateMap["refetchallhttp_btn"] = false; + // mButtonStateMap["cachewrite_btn"] = false; + //} + //else { mButtonStateMap["cacheread_btn"] = true; mButtonStateMap["http_btn"] = true; @@ -214,7 +271,9 @@ void LLFloaterTextureFetchDebugger::onClickClose() setVisible(FALSE); //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); + + delete this; } void LLFloaterTextureFetchDebugger::onClickClear() @@ -232,7 +291,7 @@ void LLFloaterTextureFetchDebugger::onClickClear() updateButtons(); //stop everything - mDebugger->stopDebug(); + mDebugger->setStopDebug(); mDebugger->clearHistory(); } @@ -240,63 +299,63 @@ void LLFloaterTextureFetchDebugger::onClickCacheRead() { disableButtons(); - mDebugger->debugCacheRead(); + setStartStatus((S32)LLTextureFetchDebugger::READ_CACHE); } void LLFloaterTextureFetchDebugger::onClickCacheWrite() { disableButtons(); - mDebugger->debugCacheWrite(); + setStartStatus((S32)LLTextureFetchDebugger::WRITE_CACHE); } void LLFloaterTextureFetchDebugger::onClickHTTPLoad() { disableButtons(); - mDebugger->debugHTTP(); + setStartStatus((S32)LLTextureFetchDebugger::HTTP_FETCHING); } void LLFloaterTextureFetchDebugger::onClickDecode() { disableButtons(); - mDebugger->debugDecoder(); + setStartStatus((S32)LLTextureFetchDebugger::DECODING); } void LLFloaterTextureFetchDebugger::onClickGLTexture() { disableButtons(); - mDebugger->debugGLTextureCreation(); + setStartStatus((S32)LLTextureFetchDebugger::GL_TEX); } void LLFloaterTextureFetchDebugger::onClickRefetchVisCache() { disableButtons(); - mDebugger->debugRefetchVisibleFromCache(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_VIS_CACHE); } void LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP() { disableButtons(); - mDebugger->debugRefetchVisibleFromHTTP(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_VIS_HTTP); } void LLFloaterTextureFetchDebugger::onClickRefetchAllCache() { disableButtons(); - mDebugger->debugRefetchAllFromCache(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_ALL_CACHE); } void LLFloaterTextureFetchDebugger::onClickRefetchAllHTTP() { disableButtons(); - mDebugger->debugRefetchAllFromHTTP(); + setStartStatus((S32)LLTextureFetchDebugger::REFETCH_ALL_HTTP); } void LLFloaterTextureFetchDebugger::draw() diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h index da1bff19d5..096ad88e07 100644 --- a/indra/newview/llfloatertexturefetchdebugger.h +++ b/indra/newview/llfloatertexturefetchdebugger.h @@ -65,9 +65,12 @@ private: void updateButtons(); void disableButtons(); + void setStartStatus(S32 status); + bool idleStart(); private: LLTextureFetchDebugger* mDebugger; std::map mButtonStateMap; + S32 mStartStatus; }; #endif // LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5e2141caf6..332b5e6f6a 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -695,7 +695,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mMetricsStartTime(0) { mCanUseNET = mUrl.empty() ; - + calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; // llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; @@ -953,7 +953,7 @@ bool LLTextureFetchWorker::doWork(S32 param) offset, size, responder); mCacheReadTimer.reset(); } - else if(!mUrl.empty() && mCanUseHTTP && mFetcher->canLoadFromNetwork()) + else if(!mUrl.empty() && mCanUseHTTP) { if (!(mUrl.compare(0, 7, "http://") == 0)) { @@ -963,15 +963,11 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = SEND_HTTP_REQ; } - else if(mFetcher->canLoadFromNetwork()) + else { setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = LOAD_FROM_NETWORK; } - else - { - return true; //failed - } } if (mLoaded) @@ -1019,15 +1015,12 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; } // need more data - else if(mFetcher->canLoadFromNetwork()) + else { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; mState = LOAD_FROM_NETWORK; } - else - { - return true; //failed - } + // fall through LLTextureFetch::sCacheHitRate.addValue(0.f); } @@ -2306,6 +2299,10 @@ S32 LLTextureFetch::update(F32 max_time_ms) commonUpdate(); } + if(mFetchDebugger) + { + mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. + } return res; } @@ -3192,7 +3189,8 @@ LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextur LLTextureFetchDebugger::~LLTextureFetchDebugger() { mFetchingHistory.clear(); - stopDebug(); + mStopDebug = TRUE; + tryToStopDebug(); } void LLTextureFetchDebugger::init() @@ -3228,6 +3226,8 @@ void LLTextureFetchDebugger::init() mRefetchedAllPixels = 0; mFreezeHistory = FALSE; + mStopDebug = FALSE; + mClearHistory = FALSE; } void LLTextureFetchDebugger::startDebug() @@ -3235,6 +3235,7 @@ void LLTextureFetchDebugger::startDebug() //lock the fetcher mFetcher->lockFetcher(true); mFreezeHistory = TRUE; + mFetcher->resetLoadSource(); //clear the current fetching queue gTextureList.clearFetchingRequests(); @@ -3290,8 +3291,13 @@ void LLTextureFetchDebugger::startDebug() mNumFetchedTextures = fetched_textures.size(); } -void LLTextureFetchDebugger::stopDebug() +void LLTextureFetchDebugger::tryToStopDebug() { + if(!mStopDebug) + { + return; + } + //clear the current debug work S32 size = mFetchingHistory.size(); switch(mState) @@ -3320,35 +3326,50 @@ void LLTextureFetchDebugger::stopDebug() break; case GL_TEX: break; + case REFETCH_VIS_CACHE: + break; + case REFETCH_VIS_HTTP: + break; + case REFETCH_ALL_CACHE: + mRefetchList.clear(); + break; + case REFETCH_ALL_HTTP: + mRefetchList.clear(); + break; default: break; } - while(1) + if(update()) { - if(update()) + //unlock the fetcher + mFetcher->lockFetcher(false); + mFetcher->resetLoadSource(); + mFreezeHistory = FALSE; + mStopDebug = FALSE; + + if(mClearHistory) { - break; + mFetchingHistory.clear(); + init(); + mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset } } - - //unlock the fetcher - mFetcher->lockFetcher(false); - mFetcher->resetLoadSource(); - mFreezeHistory = FALSE; - mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset - mRefetchList.clear(); } //called in the main thread and when the fetching queue is empty void LLTextureFetchDebugger::clearHistory() { - mFetchingHistory.clear(); - init(); + mClearHistory = TRUE; } void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker) { + if(worker->mRawImage.isNull() || worker->mFormattedImage.isNull()) + { + return; + } + if(mFreezeHistory) { if(mState == REFETCH_VIS_CACHE || mState == REFETCH_VIS_HTTP) @@ -3517,6 +3538,12 @@ void LLTextureFetchDebugger::debugHTTP() S32 LLTextureFetchDebugger::fillCurlQueue() { + if(mStopDebug) //stop + { + mNbCurlCompleted = mFetchingHistory.size(); + return 0; + } + if (mNbCurlRequests == 24) return mNbCurlRequests; @@ -3624,6 +3651,15 @@ void LLTextureFetchDebugger::makeRefetchList() void LLTextureFetchDebugger::scanRefetchList() { + if(mStopDebug) + { + return; + } + if(!mRefetchNonVis) + { + return; + } + for(std::map< LLPointer, std::vector >::iterator iter = mRefetchList.begin(); iter != mRefetchList.end(); ) { @@ -3646,14 +3682,13 @@ void LLTextureFetchDebugger::debugRefetchVisibleFromCache() mState = REFETCH_VIS_CACHE; clearTextures(); - mFetcher->setLoadSource(LLTextureFetch::FROM_CACHE_ONLY); - + mFetcher->setLoadSource(LLTextureFetch::FROM_ALL); + mTimer.reset(); mFetcher->lockFetcher(false); mRefetchVisCacheTime = -1.f; mRefetchedVisData = 0; mRefetchedVisPixels = 0; - mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); } void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() @@ -3661,16 +3696,14 @@ void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP() llassert_always(mState == IDLE); mState = REFETCH_VIS_HTTP; - clearCache(); clearTextures(); - mFetcher->setLoadSource(LLTextureFetch::FROM_NETWORK_ONLY); + mFetcher->setLoadSource(LLTextureFetch::FROM_HTTP_ONLY); mTimer.reset(); mFetcher->lockFetcher(false); mRefetchVisHTTPTime = -1.f; mRefetchedVisData = 0; mRefetchedVisPixels = 0; - mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); } void LLTextureFetchDebugger::debugRefetchAllFromCache() @@ -3680,14 +3713,14 @@ void LLTextureFetchDebugger::debugRefetchAllFromCache() clearTextures(); makeRefetchList(); - mFetcher->setLoadSource(LLTextureFetch::FROM_CACHE_ONLY); + mFetcher->setLoadSource(LLTextureFetch::FROM_ALL); mTimer.reset(); mFetcher->lockFetcher(false); mRefetchAllCacheTime = -1.f; mRefetchedAllData = 0; mRefetchedAllPixels = 0; - mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); + mRefetchNonVis = FALSE; } void LLTextureFetchDebugger::debugRefetchAllFromHTTP() @@ -3695,17 +3728,16 @@ void LLTextureFetchDebugger::debugRefetchAllFromHTTP() llassert_always(mState == IDLE); mState = REFETCH_ALL_HTTP; - clearCache(); clearTextures(); makeRefetchList(); - mFetcher->setLoadSource(LLTextureFetch::FROM_NETWORK_ONLY); + mFetcher->setLoadSource(LLTextureFetch::FROM_HTTP_ONLY); mTimer.reset(); mFetcher->lockFetcher(false); mRefetchAllHTTPTime = -1.f; mRefetchedAllData = 0; mRefetchedAllPixels = 0; - mRefetchStartTime = gDebugTimers[0].getElapsedTimeF32(); + mRefetchNonVis = TRUE; } bool LLTextureFetchDebugger::update() @@ -3751,7 +3783,7 @@ bool LLTextureFetchDebugger::update() case REFETCH_VIS_CACHE: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; + mRefetchVisCacheTime = mTimer.getElapsedTimeF32() ; mState = IDLE; mFetcher->lockFetcher(true); mFetcher->resetLoadSource(); @@ -3760,7 +3792,7 @@ bool LLTextureFetchDebugger::update() case REFETCH_VIS_HTTP: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; + mRefetchVisHTTPTime = mTimer.getElapsedTimeF32() ; mState = IDLE; mFetcher->lockFetcher(true); mFetcher->resetLoadSource(); @@ -3770,22 +3802,31 @@ bool LLTextureFetchDebugger::update() scanRefetchList(); if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchAllCacheTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; - mState = IDLE; + if(!mRefetchNonVis) + { + mRefetchNonVis = TRUE; //start to fetch non-vis + scanRefetchList(); + break; + } + + mRefetchAllCacheTime = mTimer.getElapsedTimeF32() ; + mState = IDLE; mFetcher->lockFetcher(true); mFetcher->resetLoadSource(); mRefetchList.clear(); + mRefetchNonVis = FALSE; } break; case REFETCH_ALL_HTTP: scanRefetchList(); if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchAllHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mRefetchStartTime; + mRefetchAllHTTPTime = mTimer.getElapsedTimeF32() ; mState = IDLE; mFetcher->lockFetcher(true); mFetcher->resetLoadSource(); mRefetchList.clear(); + mRefetchNonVis = FALSE; } break; default: diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index ff78d34eda..436306398e 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -221,8 +221,7 @@ public: enum e_tex_source { FROM_ALL = 0, - FROM_CACHE_ONLY, - FROM_NETWORK_ONLY, + FROM_HTTP_ONLY, INVALID_SOURCE }; private: @@ -240,8 +239,7 @@ public: void setLoadSource(e_tex_source source) {mFetchSource = source;} void resetLoadSource() {mFetchSource = mOriginFetchSource;} - bool canLoadFromCache() { return mFetchSource != FROM_NETWORK_ONLY;} - bool canLoadFromNetwork() { return mFetchSource != FROM_CACHE_ONLY;} + bool canLoadFromCache() { return mFetchSource != FROM_HTTP_ONLY;} }; //debug use @@ -257,6 +255,7 @@ public: enum e_debug_state { IDLE = 0, + START_DEBUG, READ_CACHE, WRITE_CACHE, DECODING, @@ -347,13 +346,15 @@ private: U32 mRefetchedAllPixels; BOOL mFreezeHistory; + BOOL mStopDebug; + BOOL mClearHistory; + BOOL mRefetchNonVis; std::string mHTTPUrl; S32 mNbCurlRequests; S32 mNbCurlCompleted; std::map< LLPointer, std::vector > mRefetchList; - F32 mRefetchStartTime; public: bool update(); //called in the main thread once per frame @@ -364,7 +365,8 @@ public: void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;} void startDebug(); - void stopDebug(); //stop everything + void setStopDebug() {mStopDebug = TRUE;} + void tryToStopDebug(); //stop everything void debugCacheRead(); void debugCacheWrite(); void debugHTTP(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 684d1afa4f..511fc13973 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -409,7 +409,11 @@ void LLViewerTextureManager::cleanup() void LLViewerTexture::initClass() { LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ; - sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio"); + + if(gSavedSettings.getBOOL("TextureFetchDebuggerEnabled")) + { + sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio"); + } } // static @@ -2168,7 +2172,10 @@ bool LLViewerFetchedTexture::updateFetch() void LLViewerFetchedTexture::clearFetchedResults() { - llassert_always(!mNeedsCreateTexture && !mIsFetching); + if(mNeedsCreateTexture || mIsFetching) + { + return ; + } cleanup(); destroyGLTexture(); diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml index ca2f9bfd94..6fbdd8a318 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml @@ -266,7 +266,7 @@ width="264"> - -- cgit v1.2.3 From 74d59e7128bb02a4b49af99e44f437a736a3f62b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 7 May 2012 15:16:31 -0400 Subject: Build llcorehttp as part of a viewer dependency with unit tests. This required boost::thread and the easiest path to that was to go with the 1.48 Boost release in the 3P tree (eliminating a fork for a modified 1.45 packaging). One unit test, the most important one, is failing in test_httprequest but that can be attended to later. This test issues a GET to http://localhost:2/ and that is hitting the wire but the libcurl plumbing isn't delivering the failure, only the eventual timeout. An unexpected change in behavior. --- autobuild.xml | 12 ++--- indra/cmake/Boost.cmake | 23 ++++++---- indra/cmake/LLCoreHttp.cmake | 2 + indra/cmake/LLPrimitive.cmake | 8 ++-- indra/llcommon/linden_common.h | 4 ++ indra/llcorehttp/CMakeLists.txt | 30 ++++++------ indra/llcorehttp/_httpoprequest.cpp | 2 + indra/llcorehttp/_thread.h | 4 +- indra/llcorehttp/httpcommon.h | 2 + indra/llcorehttp/tests/all_test.cpp | 64 -------------------------- indra/llcorehttp/tests/llcorehttp_test.cpp | 66 +++++++++++++++++++++++++++ indra/llcorehttp/tests/test_bufferarray.hpp | 3 +- indra/llcorehttp/tests/test_httpheaders.hpp | 2 +- indra/llcorehttp/tests/test_httpoperation.hpp | 2 +- indra/llcorehttp/tests/test_httprequest.hpp | 12 ++--- indra/llcorehttp/tests/test_httpstatus.hpp | 2 +- indra/llcorehttp/tests/test_refcounted.hpp | 2 +- 17 files changed, 131 insertions(+), 109 deletions(-) delete mode 100644 indra/llcorehttp/tests/all_test.cpp create mode 100644 indra/llcorehttp/tests/llcorehttp_test.cpp diff --git a/autobuild.xml b/autobuild.xml index 9914be6867..5c9af29b99 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ archive hash - d98078791ce345bf6168ce9ba53ca2d7 + 0c4678ac85395f5f5294b63da1d79007 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/222752/arch/Darwin/installer/boost-1.45.0-darwin-20110304.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Darwin/installer/boost-1.48.0-darwin-20120208.tar.bz2 name darwin @@ -198,9 +198,9 @@ archive hash - a34e7fffdb94a6a4d8a2966b1f216da3 + 848766eac189e0fa785f4a025532acd9 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-linux-20110310.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Linux/installer/boost-1.48.0-linux-20120208.tar.bz2 name linux @@ -210,9 +210,9 @@ archive hash - 98be22c8833aa2bca184b9fa09fbb82b + cd1e60a00d40f4475ae5e0aca86f74c1 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/CYGWIN/installer/boost-1.48.0-windows-20120208.tar.bz2 name windows diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 2135f0584c..24de1fc0c2 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -12,12 +12,13 @@ if (STANDALONE) set(BOOST_SIGNALS_LIBRARY boost_signals-mt) set(BOOST_SYSTEM_LIBRARY boost_system-mt) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) else (STANDALONE) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) if (WINDOWS) - set(BOOST_VERSION 1_45) + set(BOOST_VERSION 1_48) if(MSVC80) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc80-mt-${BOOST_VERSION} @@ -37,22 +38,26 @@ else (STANDALONE) else(MSVC80) # MSVC 10.0 config set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized libboost_program_options-vc100-mt-${BOOST_VERSION} - debug libboost_program_options-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_program_options-mt + debug libboost_program_options-mt-gd) set(BOOST_REGEX_LIBRARY - optimized libboost_regex-vc100-mt-${BOOST_VERSION} - debug libboost_regex-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_regex-mt + debug libboost_regex-mt-gd) set(BOOST_SYSTEM_LIBRARY - optimized libboost_system-vc100-mt-${BOOST_VERSION} - debug libboost_system-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_system-mt + debug libboost_system-mt-gd) set(BOOST_FILESYSTEM_LIBRARY - optimized libboost_filesystem-vc100-mt-${BOOST_VERSION} - debug libboost_filesystem-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_filesystem-mt + debug libboost_filesystem-mt-gd) + set(BOOST_THREAD_LIBRARY + optimized libboost_thread-mt + debug libboost_thread-mt-gd) endif (MSVC80) elseif (DARWIN OR LINUX) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) set(BOOST_REGEX_LIBRARY boost_regex) set(BOOST_SYSTEM_LIBRARY boost_system) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) + set(BOOST_THREAD_LIBRARY boost_thread) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake index 280189da3d..61e4b23d98 100644 --- a/indra/cmake/LLCoreHttp.cmake +++ b/indra/cmake/LLCoreHttp.cmake @@ -3,12 +3,14 @@ include(CARes) include(CURL) include(OpenSSL) +include(Boost) set(LLCOREHTTP_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llcorehttp ${CARES_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIRS} ) set(LLCOREHTTP_LIBRARIES llcorehttp) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index f15a2c2649..ab39cbb6be 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -15,10 +15,10 @@ if (WINDOWS) optimized llprimitive debug libcollada14dom22-d optimized libcollada14dom22 - debug libboost_filesystem-vc100-mt-gd-1_45 - optimized libboost_filesystem-vc100-mt-1_45 - debug libboost_system-vc100-mt-gd-1_45 - optimized libboost_system-vc100-mt-1_45 + debug libboost_filesystem-mt-gd + optimized libboost_filesystem-mt + debug libboost_system-mt-gd + optimized libboost_system-mt ) else (WINDOWS) set(LLPRIMITIVE_LIBRARIES diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index bdcc98e402..50db03cb6a 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -82,4 +82,8 @@ #include "llfile.h" #include "llformat.h" +// Boost 1.45 had version 2 as the default for the filesystem library, +// 1.48 has version 3 as the default. Keep compatibility for now. +#define BOOST_FILESYSTEM_VERSION 2 + #endif diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index e0d1817781..a950e9180c 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -73,23 +73,24 @@ target_link_libraries( ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} ) # tests -#if (LL_TESTS) -if (LL_TESTS AND 0) +if (LL_TESTS) +#if (LL_TESTS AND 0) SET(llcorehttp_TEST_SOURCE_FILES - test_allocator.cpp + tests/test_allocator.cpp ) set(llcorehttp_TEST_HEADER_FILS - test_httpstatus.hpp - test_refcounted.hpp - test_httpoperation.hpp - test_httprequest.hpp - test_httprequestqueue.hpp - test_httpheaders.hpp - test_bufferarray.hpp + tests/test_httpstatus.hpp + tests/test_refcounted.hpp + tests/test_httpoperation.hpp + tests/test_httprequest.hpp + tests/test_httprequestqueue.hpp + tests/test_httpheaders.hpp + tests/test_bufferarray.hpp ) set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} @@ -105,13 +106,16 @@ if (LL_TESTS AND 0) ${WINDOWS_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ) - LL_ADD_INTEGRATION_TEST(all + LL_ADD_INTEGRATION_TEST(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}" "${test_libs}" ) -#endif (LL_TESTS) -endif (LL_TESTS AND 0) +endif (LL_TESTS) +#endif (LL_TESTS AND 0) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 316df8bd49..3c9eb71b9a 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -259,6 +259,8 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) HttpStatus status; mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 0937d698c7..46a333a749 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -27,9 +27,11 @@ #ifndef LLCOREINT_THREAD_H_ #define LLCOREINT_THREAD_H_ +#include +#include -#include "_refcounted.h" +#include "_refcounted.h" namespace LLCoreInt { diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index cd7c09f097..5fc497e720 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -93,6 +93,8 @@ /// /// +#include "linden_common.h" + #include diff --git a/indra/llcorehttp/tests/all_test.cpp b/indra/llcorehttp/tests/all_test.cpp deleted file mode 100644 index 636f6f8c05..0000000000 --- a/indra/llcorehttp/tests/all_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file test_all - * @brief Main test runner - * - * $LicenseInfo:firstyear=2012&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#include - -#include -#include - -#include - -// Pull in each of the test sets -#include "test_httpstatus.hpp" -#include "test_refcounted.hpp" -#include "test_httpoperation.hpp" -#include "test_httprequest.hpp" -#include "test_httpheaders.hpp" -#include "test_bufferarray.hpp" -#include "test_httprequestqueue.hpp" - - -namespace tut -{ - test_runner_singleton runner; -} - -int main() -{ - curl_global_init(CURL_GLOBAL_ALL); - - // *FIXME: Need threaded/SSL curl setup here. - - tut::reporter reporter; - - tut::runner.get().set_callback(&reporter); - tut::runner.get().run_tests(); - return !reporter.all_ok(); - - curl_global_cleanup(); -} - diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp new file mode 100644 index 0000000000..ad596d61cc --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -0,0 +1,66 @@ +/** + * @file llcorehttp_test + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include + +#include +#include + +#include + +// Pull in each of the test sets +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_bufferarray.hpp" +#include "test_httprequestqueue.hpp" + +#if 0 + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + +#endif diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 4f5d0284a1..ecbb5ef250 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCORE_BUFFER_ARRAY_H_ #define TEST_LLCORE_BUFFER_ARRAY_H_ -#include +#include "bufferarray.h" #include @@ -183,7 +183,6 @@ void BufferArrayTestObjectType::test<4>() char str1[] = "abcdefghij"; size_t str1_len(strlen(str1)); char str2[] = "ABCDEFGHIJ"; - size_t str2_len(strlen(str2)); char buffer[256]; size_t len = ba->write(str1, str1_len); diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp index d22b516691..ce0d19b058 100644 --- a/indra/llcorehttp/tests/test_httpheaders.hpp +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCORE_HTTP_HEADERS_H_ #define TEST_LLCORE_HTTP_HEADERS_H_ -#include +#include "httpheaders.h" #include diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index c9feaddb2a..6c3df1e9e3 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -27,7 +27,7 @@ #define TEST_LLCORE_HTTP_OPERATION_H_ #include "_httpoperation.h" -#include +#include "httphandler.h" #include diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index df5640859f..ab25a2eb1a 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -26,11 +26,11 @@ #ifndef TEST_LLCORE_HTTP_REQUEST_H_ #define TEST_LLCORE_HTTP_REQUEST_H_ -#include -#include -#include -#include -#include +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "_httpservice.h" +#include "_httprequestqueue.h" #include @@ -360,7 +360,7 @@ void HttpRequestTestObjectType::test<5>() mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0.0f, - "http://localhost:2/nothing/here", + "http://127.0.0.1:2/nothing/here", 0, 0, NULL, diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index fa5803a17c..38bf494dec 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -27,7 +27,7 @@ #ifndef TEST_HTTP_STATUS_H_ #define TEST_HTTP_STATUS_H_ -#include +#include "httpcommon.h" #include #include diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp index 6a17947288..cb4b50287a 100644 --- a/indra/llcorehttp/tests/test_refcounted.hpp +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -26,7 +26,7 @@ #ifndef TEST_LLCOREINT_REF_COUNTED_H_ #define TEST_LLCOREINT_REF_COUNTED_H_ -#include <_refcounted.h> +#include "_refcounted.h" #include "test_allocator.h" -- cgit v1.2.3 From 327970a2169998bc8c01d6c3a88a5b3799b82502 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 7 May 2012 13:32:13 -0700 Subject: Fix Mac and Linux compile error in lltexturefetch.cpp --- indra/newview/lltexturefetch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 332b5e6f6a..541f9bd12b 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -3666,7 +3666,7 @@ void LLTextureFetchDebugger::scanRefetchList() if(iter->second.empty()) { gTextureList.setDebugFetching(iter->first, -1); - iter = mRefetchList.erase(iter); + mRefetchList.erase(iter++); // This is the correct method to "erase and move on" in an std::map } else { -- cgit v1.2.3 From 3eb139419c806f1aa68ba86bf3a44b53ef2c1102 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 7 May 2012 13:53:39 -0700 Subject: SH-3126 : Clean up stray comment --- indra/newview/lltexturefetch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a25876a1e6..c7f522fc68 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -780,7 +780,6 @@ U32 LLTextureFetchWorker::calcWorkPriority() } // mWorkMutex is locked -// Merov : Change so to take into account size == 0 == max void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; -- cgit v1.2.3 From ccb86a2a6675b21dda6267d4a850e5a09acbafa4 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 7 May 2012 18:51:33 -0700 Subject: SH-3126 : Cleanup image completion test as per Bao's review --- indra/newview/lltexturefetch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index c7f522fc68..60d38e7436 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1714,7 +1714,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); buffer->readAfter(channels.in(), NULL, mBuffer, data_size); mBufferSize += data_size; - if ((data_size < mRequestedSize) || (mRequestedSize == 0)) + if (mRequestedSize == 0) { mHaveAllData = TRUE; } -- cgit v1.2.3 From 7caef4bc6c348a4aad4a777df0d1ea34ab13ff00 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 10:38:08 -0400 Subject: Okay, got Mac building with Boost 1.48. Unit tests needed NULL pointer defenses in the delete functions of the allocation support. General boost library renaming again. Linux builds in TC though it shouldn't based on what Boost.cmake lookes like... --- indra/cmake/Boost.cmake | 31 ++++++++++++++++++++++++------ indra/llcorehttp/CMakeLists.txt | 2 -- indra/llcorehttp/tests/llcorehttp_test.cpp | 6 ++++-- indra/llcorehttp/tests/test_allocator.cpp | 10 ++++++++-- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 24de1fc0c2..4a797d968a 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -53,11 +53,30 @@ else (STANDALONE) optimized libboost_thread-mt debug libboost_thread-mt-gd) endif (MSVC80) - elseif (DARWIN OR LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) - set(BOOST_REGEX_LIBRARY boost_regex) - set(BOOST_SYSTEM_LIBRARY boost_system) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) - set(BOOST_THREAD_LIBRARY boost_thread) + elseif (LINUX) + set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) + set(BOOST_REGEX_LIBRARY boost_regex-mt) + set(BOOST_SYSTEM_LIBRARY boost_system-mt) + set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) + elseif (DARWIN) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a950e9180c..99b60a5995 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -78,7 +78,6 @@ target_link_libraries( # tests if (LL_TESTS) -#if (LL_TESTS AND 0) SET(llcorehttp_TEST_SOURCE_FILES tests/test_allocator.cpp ) @@ -117,5 +116,4 @@ if (LL_TESTS) ) endif (LL_TESTS) -#endif (LL_TESTS AND 0) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index ad596d61cc..92f16be8fd 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -27,8 +27,10 @@ #include -#include -#include +// These are not the right way in viewer for some reason: +// #include +// #include +#include "../test/lltut.h" #include diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp index 926de0a208..ea12dc58eb 100644 --- a/indra/llcorehttp/tests/test_allocator.cpp +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -166,13 +166,19 @@ void * operator new[](std::size_t size) THROW_BAD_ALLOC() void operator delete(void * p) THROW_NOTHING() { - FreeMem( p ); + if (p) + { + FreeMem( p ); + } } void operator delete[](void * p) THROW_NOTHING() { - FreeMem( p ); + if (p) + { + FreeMem( p ); + } } -- cgit v1.2.3 From 239e072bfcf97b8a12c18ff9974fd0a2929c9ee4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 12:27:24 -0400 Subject: Unit test still giving me issues on the local windows system. Seems to be a hard stall while allocating the first easy handle in a descent of the global initiailization code but that doesn't seem to be a problem on TC machines. Perhaps the static linking is creating multiple data copies. More work needed. --- indra/cmake/Boost.cmake | 20 +++++++++++++++----- indra/llcorehttp/CMakeLists.txt | 8 +++++++- indra/llcorehttp/_httplibcurl.h | 1 + indra/llcorehttp/_httpoprequest.h | 2 ++ indra/llcorehttp/httpcommon.h | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 4a797d968a..2af0bc1b30 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -54,11 +54,21 @@ else (STANDALONE) debug libboost_thread-mt-gd) endif (MSVC80) elseif (LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) - set(BOOST_REGEX_LIBRARY boost_regex-mt) - set(BOOST_SYSTEM_LIBRARY boost_system-mt) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) - set(BOOST_THREAD_LIBRARY boost_thread-mt) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) elseif (DARWIN) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized boost_program_options-mt diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 99b60a5995..81c502b642 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -3,8 +3,12 @@ project(llcorehttp) include(00-Common) -include(LLCoreHttp) include(GoogleMock) +include(CURL) +include(CARes) +include(OpenSSL) +include(ZLIB) +include(LLCoreHttp) include(LLAddBuildTest) include(LLCommon) include(Tut) @@ -106,8 +110,10 @@ if (LL_TESTS) ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} + ${CARES_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} ) LL_ADD_INTEGRATION_TEST(llcorehttp diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 5ba244cee4..01c68320af 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -27,6 +27,7 @@ #ifndef _LLCORE_HTTP_LIBCURL_H_ #define _LLCORE_HTTP_LIBCURL_H_ +#include "linden_common.h" // Modifies curl/curl.h interfaces #include #include diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index e912851aed..232ee841d6 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -28,6 +28,8 @@ #define _LLCORE_HTTP_OPREQUEST_H_ +#include "linden_common.h" // Modifies curl/curl.h interfaces + #include "httpcommon.h" #include diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 5fc497e720..9de5769d57 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -93,7 +93,7 @@ /// /// -#include "linden_common.h" +#include "linden_common.h" // Modifies curl/curl.h interfaces #include -- cgit v1.2.3 From 29da931051202a10ea71db56e47acecae1603a5d Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 8 May 2012 10:16:14 -0700 Subject: SH-3047 : Tweak of the performance compression data gathering code --- indra/llimage/llimagej2c.cpp | 6 +++++- indra/llimage/llimagej2c.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp index a0e90b6b08..3c3cf980ca 100755 --- a/indra/llimage/llimagej2c.cpp +++ b/indra/llimage/llimagej2c.cpp @@ -464,6 +464,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester addMetric("Perf Compression (kB/s)"); mRunBytesInDecompression = 0; + mRunBytesOutDecompression = 0; mRunBytesInCompression = 0; mTotalBytesInDecompression = 0; @@ -556,13 +557,16 @@ void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const mTotalBytesInDecompression += bytesIn; mRunBytesInDecompression += bytesIn; mTotalBytesOutDecompression += bytesOut; + mRunBytesOutDecompression += bytesOut; //if (mRunBytesInDecompression > (1000000)) - if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f)) + if (mRunBytesOutDecompression > (10000000)) + //if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f)) { // Output everything outputTestResults(); // Reset the decompression data of the run mRunBytesInDecompression = 0; + mRunBytesOutDecompression = 0; mRunTimeDecompression = mTotalTimeDecompression; } } diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h index 42093e0e64..ce8195940d 100644 --- a/indra/llimage/llimagej2c.h +++ b/indra/llimage/llimagej2c.h @@ -156,7 +156,8 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic U32 mTotalBytesOutDecompression; // Total bytes produced by decompressor U32 mTotalBytesInCompression; // Total bytes fed to compressor U32 mTotalBytesOutCompression; // Total bytes produced by compressor - U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run + U32 mRunBytesInDecompression; // Bytes fed to decompressor in this run + U32 mRunBytesOutDecompression; // Bytes produced by the decompressor in this run U32 mRunBytesInCompression; // Bytes fed to compressor in this run // // Time -- cgit v1.2.3 From cc878fed1b65779cd4535678117e99a68a69c62c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 11:32:46 -0700 Subject: Now trying to get linux working. libllcommon is picking up .so boost libraries when we tend to want statics. Try to work around that for the moment. --- indra/newview/viewer_manifest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0931c4ec9b..b3ca24a783 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1031,6 +1031,8 @@ class Linux_i686Manifest(LinuxManifest): self.path("libaprutil-1.so") self.path("libaprutil-1.so.0") self.path("libaprutil-1.so.0.3.10") + self.path("libboost_program_options-mt.so.1.48.0") + self.path("libboost_regex.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From b7d3ae7dce78b6541f871eaca7152cc86cde5840 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 12:17:37 -0700 Subject: More linux work. Think I found the stager. --- indra/cmake/Copy3rdPartyLibs.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 394db362b1..ee6a3f68da 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -251,20 +251,22 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so + libboost_program_options-mt.so + libboost_regex-mt.so libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 libdb-5.1.so libexpat.so libexpat.so.1 - libglod.so + libglod.so libgmock_main.so libgmock.so.0 libgmodule-2.0.so libgobject-2.0.so libgtest_main.so libgtest.so.0 - libminizip.so + libminizip.so libopenal.so libopenjpeg.so libssl.so -- cgit v1.2.3 From c5be6d0b92642b3ba12a12441b6b436cde18dcdb Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 13:15:42 -0700 Subject: Okay, needed full version numbers. --- indra/cmake/Copy3rdPartyLibs.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ee6a3f68da..0866b35e9d 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -251,8 +251,8 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so - libboost_program_options-mt.so - libboost_regex-mt.so + libboost_program_options-mt.so.1.48.0 + libboost_regex-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 -- cgit v1.2.3 From e1a978bbcfe4640e3163fd0a3ed41460f1d6942a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 8 May 2012 13:39:06 -0700 Subject: And more boost library work. --- indra/cmake/Copy3rdPartyLibs.cmake | 1 + indra/newview/viewer_manifest.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 0866b35e9d..fe42cc5251 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -253,6 +253,7 @@ elseif(LINUX) libatk-1.0.so libboost_program_options-mt.so.1.48.0 libboost_regex-mt.so.1.48.0 + libboost_thread-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index b3ca24a783..2be89b77de 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1032,7 +1032,8 @@ class Linux_i686Manifest(LinuxManifest): self.path("libaprutil-1.so.0") self.path("libaprutil-1.so.0.3.10") self.path("libboost_program_options-mt.so.1.48.0") - self.path("libboost_regex.so.1.48.0") + self.path("libboost_regex-mt.so.1.48.0") + self.path("libboost_thread-mt.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From 5caea1bcae24688bf3bbc3172a811bcccd4c3016 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Tue, 8 May 2012 16:02:24 -0600 Subject: For SH-3086. Make the fetch debugger not to stall the main thread. --- indra/newview/llfloatertexturefetchdebugger.cpp | 70 +++----------- indra/newview/lltexturefetch.cpp | 120 +++++++++++++++++++++--- indra/newview/lltexturefetch.h | 33 ++++--- 3 files changed, 137 insertions(+), 86 deletions(-) diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp index 02872f54a0..9157389187 100644 --- a/indra/newview/llfloatertexturefetchdebugger.cpp +++ b/indra/newview/llfloatertexturefetchdebugger.cpp @@ -134,46 +134,9 @@ void LLFloaterTextureFetchDebugger::setStartStatus(S32 status) bool LLFloaterTextureFetchDebugger::idleStart() { - switch((LLTextureFetchDebugger::e_debug_state)mStartStatus) - { - case LLTextureFetchDebugger::IDLE: - break; - case LLTextureFetchDebugger::START_DEBUG: - mDebugger->startDebug(); - break; - case LLTextureFetchDebugger::READ_CACHE: - mDebugger->debugCacheRead(); - break; - case LLTextureFetchDebugger::WRITE_CACHE: - mDebugger->debugCacheWrite(); - break; - case LLTextureFetchDebugger::DECODING: - mDebugger->debugDecoder(); - break; - case LLTextureFetchDebugger::HTTP_FETCHING: - mDebugger->debugHTTP(); - break; - case LLTextureFetchDebugger::GL_TEX: - mDebugger->debugGLTextureCreation(); - break; - case LLTextureFetchDebugger::REFETCH_VIS_CACHE: - mDebugger->debugRefetchVisibleFromCache(); - break; - case LLTextureFetchDebugger::REFETCH_VIS_HTTP: - mDebugger->debugRefetchVisibleFromHTTP(); - break; - case LLTextureFetchDebugger::REFETCH_ALL_CACHE: - mDebugger->debugRefetchAllFromCache(); - break; - case LLTextureFetchDebugger::REFETCH_ALL_HTTP: - mDebugger->debugRefetchAllFromHTTP(); - break; - default: - break; - } - if(mStartStatus != (S32)LLTextureFetchDebugger::IDLE) { + mDebugger->startWork((LLTextureFetchDebugger::e_debug_state)mStartStatus); mStartStatus = (S32)LLTextureFetchDebugger::IDLE; return true; } @@ -188,13 +151,22 @@ void LLFloaterTextureFetchDebugger::idle() return; } + const F32 max_time = 0.005f; //5ms LLTextureFetchDebugger::e_debug_state state = mDebugger->getState(); - if(mDebugger->update()) + if(mDebugger->update(max_time)) { switch(state) { case LLTextureFetchDebugger::IDLE: break; + case LLTextureFetchDebugger::START_DEBUG: + mButtonStateMap["cacheread_btn"] = true; + mButtonStateMap["http_btn"] = true; + mButtonStateMap["refetchviscache_btn"] = true; + mButtonStateMap["refetchvishttp_btn"] = true; + mButtonStateMap["refetchallcache_btn"] = true; + mButtonStateMap["refetchallhttp_btn"] = true; + break; case LLTextureFetchDebugger::READ_CACHE: mButtonStateMap["decode_btn"] = true; break; @@ -243,26 +215,6 @@ void LLFloaterTextureFetchDebugger::onClickStart() mButtonStateMap["start_btn"] = false; - //if(LLAppViewer::getTextureFetch()->canLoadFromCache()) - //{ - // mButtonStateMap["cacheread_btn"] = true; - // mButtonStateMap["http_btn"] = false; - // mButtonStateMap["refetchviscache_btn"] = true; - // mButtonStateMap["refetchvishttp_btn"] = false; - // mButtonStateMap["refetchallcache_btn"] = true; - // mButtonStateMap["refetchallhttp_btn"] = false; - // mButtonStateMap["cachewrite_btn"] = false; - //} - //else - { - mButtonStateMap["cacheread_btn"] = true; - mButtonStateMap["http_btn"] = true; - mButtonStateMap["refetchviscache_btn"] = true; - mButtonStateMap["refetchvishttp_btn"] = true; - mButtonStateMap["refetchallcache_btn"] = true; - mButtonStateMap["refetchallhttp_btn"] = true; - } - updateButtons(); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 332b5e6f6a..a4c8993ae6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -3230,6 +3230,48 @@ void LLTextureFetchDebugger::init() mClearHistory = FALSE; } +void LLTextureFetchDebugger::startWork(e_debug_state state) +{ + switch(state) + { + case IDLE: + break; + case START_DEBUG: + startDebug(); + break; + case READ_CACHE: + debugCacheRead(); + break; + case WRITE_CACHE: + debugCacheWrite(); + break; + case DECODING: + debugDecoder(); + break; + case HTTP_FETCHING: + debugHTTP(); + break; + case GL_TEX: + debugGLTextureCreation(); + break; + case REFETCH_VIS_CACHE: + debugRefetchVisibleFromCache(); + break; + case REFETCH_VIS_HTTP: + debugRefetchVisibleFromHTTP(); + break; + case REFETCH_ALL_CACHE: + debugRefetchAllFromCache(); + break; + case REFETCH_ALL_HTTP: + debugRefetchAllFromHTTP(); + break; + default: + break; + } + return; +} + void LLTextureFetchDebugger::startDebug() { //lock the fetcher @@ -3240,6 +3282,13 @@ void LLTextureFetchDebugger::startDebug() //clear the current fetching queue gTextureList.clearFetchingRequests(); + mState = START_DEBUG; +} + +bool LLTextureFetchDebugger::processStartDebug(F32 max_time) +{ + mTimer.reset(); + //wait for all works to be done while(1) { @@ -3251,6 +3300,11 @@ void LLTextureFetchDebugger::startDebug() { break; } + + if(mTimer.getElapsedTimeF32() > max_time) + { + return false; + } } //collect statistics @@ -3289,6 +3343,8 @@ void LLTextureFetchDebugger::startDebug() } mNumFetchedTextures = fetched_textures.size(); + + return true; } void LLTextureFetchDebugger::tryToStopDebug() @@ -3340,7 +3396,7 @@ void LLTextureFetchDebugger::tryToStopDebug() break; } - if(update()) + if(update(0.005f)) { //unlock the fetcher mFetcher->lockFetcher(false); @@ -3582,7 +3638,7 @@ void LLTextureFetchDebugger::debugGLTextureCreation() { llassert_always(mState == IDLE); mState = GL_TEX; - std::vector tex_list; + mTempTexList.clear(); S32 size = mFetchingHistory.size(); for(S32 i = 0 ; i < size ; i++) @@ -3593,30 +3649,54 @@ void LLTextureFetchDebugger::debugGLTextureCreation() if(tex && !tex->isForSculptOnly()) { tex->destroyGLTexture() ; - tex_list.push_back(tex); + mTempTexList.push_back(tex); } } } + + mGLCreationTime = -1.f; + mTempIndex = 0; + mHistoryListIndex = 0; + + return; +} +bool LLTextureFetchDebugger::processGLCreation(F32 max_time) +{ mTimer.reset(); - mGLCreationTime = -1.f; - S32 j = 0 ; - S32 size1 = tex_list.size(); - for(S32 i = 0 ; i < size && j < size1; i++) + bool done = true; + S32 size = mFetchingHistory.size(); + S32 size1 = mTempTexList.size(); + for(; mHistoryListIndex < size && mTempIndex < size1; mHistoryListIndex++) { - if(mFetchingHistory[i].mRawImage.notNull()) + if(mFetchingHistory[mHistoryListIndex].mRawImage.notNull()) { - if(mFetchingHistory[i].mID == tex_list[j]->getID()) + if(mFetchingHistory[mHistoryListIndex].mID == mTempTexList[mTempIndex]->getID()) { - tex_list[j]->createGLTexture(mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mRawImage, 0, TRUE, tex_list[j]->getBoostLevel()); - j++; + mTempTexList[mTempIndex]->createGLTexture(mFetchingHistory[mHistoryListIndex].mDecodedLevel, + mFetchingHistory[mHistoryListIndex].mRawImage, 0, TRUE, mTempTexList[mTempIndex]->getBoostLevel()); + mTempIndex++; } } + + if(mTimer.getElapsedTimeF32() > max_time) + { + done = false; + break; + } } - mGLCreationTime = mTimer.getElapsedTimeF32() ; - return; + if(mGLCreationTime < 0.f) + { + mGLCreationTime = mTimer.getElapsedTimeF32() ; + } + else + { + mGLCreationTime += mTimer.getElapsedTimeF32() ; + } + + return done; } //clear fetching results of all textures. @@ -3740,10 +3820,16 @@ void LLTextureFetchDebugger::debugRefetchAllFromHTTP() mRefetchNonVis = TRUE; } -bool LLTextureFetchDebugger::update() +bool LLTextureFetchDebugger::update(F32 max_time) { switch(mState) { + case START_DEBUG: + if(processStartDebug(max_time)) + { + mState = IDLE; + } + break; case READ_CACHE: if(!mTextureCache->update(1)) { @@ -3778,7 +3864,11 @@ bool LLTextureFetchDebugger::update() } break; case GL_TEX: - mState = IDLE; + if(processGLCreation(max_time)) + { + mState = IDLE; + mTempTexList.clear(); + } break; case REFETCH_VIS_CACHE: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 436306398e..d7677295c0 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -355,8 +355,12 @@ private: S32 mNbCurlCompleted; std::map< LLPointer, std::vector > mRefetchList; + std::vector< LLPointer > mTempTexList; + S32 mTempIndex; + S32 mHistoryListIndex; + public: - bool update(); //called in the main thread once per frame + bool update(F32 max_time); //called in the main thread once per frame //fetching history void clearHistory(); @@ -364,19 +368,10 @@ public: void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;} - void startDebug(); + void startWork(e_debug_state state); void setStopDebug() {mStopDebug = TRUE;} void tryToStopDebug(); //stop everything - void debugCacheRead(); - void debugCacheWrite(); - void debugHTTP(); - void debugDecoder(); - void debugGLTextureCreation(); - void debugRefetchVisibleFromCache(); - void debugRefetchVisibleFromHTTP(); - void debugRefetchAllFromCache(); - void debugRefetchAllFromHTTP(); - + void callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(S32 id, bool success); @@ -434,6 +429,20 @@ private: S32 fillCurlQueue(); + void startDebug(); + void debugCacheRead(); + void debugCacheWrite(); + void debugHTTP(); + void debugDecoder(); + void debugGLTextureCreation(); + void debugRefetchVisibleFromCache(); + void debugRefetchVisibleFromHTTP(); + void debugRefetchAllFromCache(); + void debugRefetchAllFromHTTP(); + + bool processStartDebug(F32 max_time); + bool processGLCreation(F32 max_time); + private: static bool sDebuggerEnabled; public: -- cgit v1.2.3 From 28cf5199ce783eddd89bad5b8b41318abf7c7dff Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 8 May 2012 21:42:53 -0700 Subject: SH-3047 : Fix lossless compression for small textures. --- indra/llkdu/llimagej2ckdu.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index cbfc34ebb8..53d2a2f3c5 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -619,36 +619,27 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co // This code is where we specify the target number of bytes for each quality layer. // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h - U32 i = FIRST_PACKET_SIZE; + layer_bytes[nb_layers++] = FIRST_PACKET_SIZE; + U32 i = MIN_LAYER_SIZE; while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1))) { - if (i == FIRST_PACKET_SIZE * 4) - { - // That really just means that the first layer is FIRST_PACKET_SIZE and the second is MIN_LAYER_SIZE - i = MIN_LAYER_SIZE; - } - layer_bytes[nb_layers] = i; - nb_layers++; + layer_bytes[nb_layers++] = i; i *= 4; } + if (layer_bytes[nb_layers-1] < max_bytes) + { + // Set the last quality layer if necessary so to fit the preset compression ratio + // Use 0 for that last layer for reversible images so all remaining code blocks will be flushed + layer_bytes[nb_layers++] = (reversible ? 0 : max_bytes); + } if (reversible) { codestream.access_siz()->parse_string("Creversible=yes"); - // *TODO: we should use yuv in reversible mode and one res level since those images are small. - // Don't turn this on now though as both create problems on decoding for the moment - //codestream.access_siz()->parse_string("Clevels=1"); + // *TODO: we should use yuv in reversible mode + // Don't turn this on now though as it creates problems on decoding for the moment //codestream.access_siz()->parse_string("Cycc=no"); - // In the reversible case, set the last entry of that table to 0 so that all generated bits will - // indeed be output by the time the last quality layer is encountered. - layer_bytes[nb_layers] = 0; - } - else - { - // Truncate the last quality layer if necessary so to fit the set compression ratio - layer_bytes[nb_layers] = max_bytes; } - nb_layers++; std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); -- cgit v1.2.3 From 2f496ecaeeb0ab90c29ca6f0414cad1fe16cd4b0 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 06:49:18 -0700 Subject: Add more boost libs to the lib copy and manifest. --- indra/cmake/Copy3rdPartyLibs.cmake | 3 +++ indra/newview/viewer_manifest.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index fe42cc5251..17ea48f31f 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -254,6 +254,9 @@ elseif(LINUX) libboost_program_options-mt.so.1.48.0 libboost_regex-mt.so.1.48.0 libboost_thread-mt.so.1.48.0 + libboost_filesystem-mt.so.1.48.0 + libboost_signals-mt.so.1.48.0 + libboost_system-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2be89b77de..fffb61e89c 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1034,6 +1034,9 @@ class Linux_i686Manifest(LinuxManifest): self.path("libboost_program_options-mt.so.1.48.0") self.path("libboost_regex-mt.so.1.48.0") self.path("libboost_thread-mt.so.1.48.0") + self.path("libboost_filesystem-mt.so.1.48.0") + self.path("libboost_signals-mt.so.1.48.0") + self.path("libboost_system-mt.so.1.48.0") self.path("libbreakpad_client.so.0.0.0") self.path("libbreakpad_client.so.0") self.path("libbreakpad_client.so") -- cgit v1.2.3 From 7a9acdc68a454886efc38cd4558b64856f4a9a04 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 07:06:15 -0700 Subject: Try to get some more correct curl init into the unit testing. --- indra/llcorehttp/tests/llcorehttp_test.cpp | 78 ++++++++++++++++++++++++++++- indra/llcorehttp/tests/llcorehttp_test.h | 34 +++++++++++++ indra/llcorehttp/tests/test_httprequest.hpp | 5 ++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 indra/llcorehttp/tests/llcorehttp_test.h diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 92f16be8fd..da811adb19 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -24,15 +24,18 @@ * $/LicenseInfo$ */ +#include "llcorehttp_test.h" #include // These are not the right way in viewer for some reason: // #include // #include +// This works: #include "../test/lltut.h" #include +#include // Pull in each of the test sets #include "test_httpstatus.hpp" @@ -43,7 +46,10 @@ #include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" -#if 0 +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); + +#if 0 // lltut provides main namespace tut { @@ -65,4 +71,74 @@ int main() curl_global_cleanup(); } +#endif // 0 + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +int getopt(int argc, char * const argv[], const char *optstring) +{ + return -1; +} + #endif + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h new file mode 100644 index 0000000000..941cb457d3 --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -0,0 +1,34 @@ +/** + * @file llcorehttp_test.h + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef _LLCOREHTTP_TEST_H_ +#define _LLCOREHTTP_TEST_H_ + +extern void init_curl(); +extern void term_curl(); + +#endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ab25a2eb1a..a77a36c96e 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -35,6 +35,7 @@ #include #include "test_allocator.h" +#include "llcorehttp_test.h" using namespace LLCoreInt; @@ -334,6 +335,8 @@ void HttpRequestTestObjectType::test<4>() template <> template <> void HttpRequestTestObjectType::test<5>() { + init_curl(); + set_test_name("HttpRequest GET + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -416,6 +419,8 @@ void HttpRequestTestObjectType::test<5>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + + term_curl(); } } // end namespace tut -- cgit v1.2.3 From 30d72b041f3221b903ac11c0054dc221b0c0329b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 9 May 2012 10:23:02 -0400 Subject: Added correct libcurl initialization to the unit tests which makes Windows builds reliable. It's the right thing to do and introduced a scoped version for convenience in tests. --- indra/llcorehttp/tests/llcorehttp_test.cpp | 5 +---- indra/llcorehttp/tests/llcorehttp_test.h | 25 +++++++++++++++++++++++++ indra/llcorehttp/tests/test_httprequest.hpp | 14 ++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index da811adb19..0ee767fdca 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -34,9 +34,6 @@ // This works: #include "../test/lltut.h" -#include -#include - // Pull in each of the test sets #include "test_httpstatus.hpp" #include "test_refcounted.hpp" @@ -49,7 +46,7 @@ unsigned long ssl_thread_id_callback(void); void ssl_locking_callback(int mode, int type, const char * file, int line); -#if 0 // lltut provides main +#if 0 // lltut provides main and runner namespace tut { diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index 941cb457d3..1550881a00 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -28,7 +28,32 @@ #ifndef _LLCOREHTTP_TEST_H_ #define _LLCOREHTTP_TEST_H_ +#include "linden_common.h" // Modifies curl interfaces + +#include +#include + +// Initialization and cleanup for libcurl. Mainly provides +// a mutex callback for SSL and a thread ID hash for libcurl. +// If you don't use these (or equivalent) and do use libcurl, +// you'll see stalls and other anomalies when performing curl +// operations. extern void init_curl(); extern void term_curl(); +class ScopedCurlInit +{ +public: + ScopedCurlInit() + { + init_curl(); + } + + ~ScopedCurlInit() + { + term_curl(); + } +}; + + #endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index a77a36c96e..a73d90957e 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -106,6 +106,8 @@ HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); template <> template <> void HttpRequestTestObjectType::test<1>() { + ScopedCurlInit ready; + set_test_name("HttpRequest construction"); // record the total amount of dynamically allocated memory @@ -131,6 +133,8 @@ void HttpRequestTestObjectType::test<1>() template <> template <> void HttpRequestTestObjectType::test<2>() { + ScopedCurlInit ready; + set_test_name("HttpRequest and Null Op queued"); // record the total amount of dynamically allocated memory @@ -168,6 +172,8 @@ void HttpRequestTestObjectType::test<2>() template <> template <> void HttpRequestTestObjectType::test<3>() { + ScopedCurlInit ready; + set_test_name("HttpRequest NoOp + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -246,6 +252,8 @@ void HttpRequestTestObjectType::test<3>() template <> template <> void HttpRequestTestObjectType::test<4>() { + ScopedCurlInit ready; + set_test_name("2 HttpRequest instances, one thread"); // Handler can be stack-allocated *if* there are no dangling @@ -335,8 +343,8 @@ void HttpRequestTestObjectType::test<4>() template <> template <> void HttpRequestTestObjectType::test<5>() { - init_curl(); - + ScopedCurlInit ready; + set_test_name("HttpRequest GET + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -419,8 +427,6 @@ void HttpRequestTestObjectType::test<5>() ensure("Two handler calls on the way out", 2 == mHandlerCalls); // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); - - term_curl(); } } // end namespace tut -- cgit v1.2.3 From e8ef6fd0e7d0830a5939ab7f0bbe5a7d280c719f Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Wed, 9 May 2012 17:04:02 -0700 Subject: SH-3075 : Fix reversible compression for very small textures. Also supress the forcing to reversible for small textures. --- indra/llkdu/llimagej2ckdu.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 53d2a2f3c5..cf88de12b4 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -609,13 +609,6 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co llassert (base.mRate > 0.f); max_bytes = (U32)((F32)(max_bytes) * base.mRate); - // If the image is very small, code it in a lossless way. - // Note: it'll also have only 1 layer which is fine as there's no point reordering blocks in that case. - if (max_bytes < FIRST_PACKET_SIZE) - { - reversible = true; - } - // This code is where we specify the target number of bytes for each quality layer. // We're using a logarithmic spacing rule that fits with our way of fetching texture data. // Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h @@ -626,27 +619,32 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co layer_bytes[nb_layers++] = i; i *= 4; } + // Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test if (layer_bytes[nb_layers-1] < max_bytes) { - // Set the last quality layer if necessary so to fit the preset compression ratio - // Use 0 for that last layer for reversible images so all remaining code blocks will be flushed - layer_bytes[nb_layers++] = (reversible ? 0 : max_bytes); + // Set the last quality layer so to fit the preset compression ratio + layer_bytes[nb_layers++] = max_bytes; } if (reversible) { + // Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed + // Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that + // cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen. + if ((base.getWidth() >= 32) || (base.getHeight() >= 32)) + { + layer_bytes[nb_layers++] = 0; + } codestream.access_siz()->parse_string("Creversible=yes"); // *TODO: we should use yuv in reversible mode // Don't turn this on now though as it creates problems on decoding for the moment //codestream.access_siz()->parse_string("Cycc=no"); } - + std::string layer_string = llformat("Clayers=%d",nb_layers); codestream.access_siz()->parse_string(layer_string.c_str()); // Set up data ordering, markers, etc... if precincts or blocks specified - // Note: This code is *not* used in the encoding made by the viewer. It is currently used only - // by llimage_libtest to create various j2c and test alternative compression schemes. if ((mBlocksSize != -1) || (mPrecinctsSize != -1)) { if (mPrecinctsSize != -1) -- cgit v1.2.3 From b73527420b4e664978f8d5587b7801435a7b56ca Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 10 May 2012 11:04:51 -0600 Subject: fix for SH-3132: Many "HTTP GET failed errors" with Drano merge candidate 255539 --- indra/newview/lltexturefetch.cpp | 54 ++++++++++++++++++++++++++++++++++++---- indra/newview/lltexturefetch.h | 7 ++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index eac6abc1ca..e96bcd2ad0 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1162,8 +1162,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //1, not openning too many file descriptors at the same time; //2, control the traffic of http so udp gets bandwidth. // - static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 24 ; - if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) + if(!mFetcher->canIssueHTTPRequest()) { return false ; //wait. } @@ -1275,6 +1274,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // requests instead of returning 503... we already limit the number pending. ++mHTTPFailCount; max_attempts = mHTTPFailCount+1; // Keep retrying + mFetcher->adjustHTTPConcurrency(false); LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } else @@ -1282,6 +1282,7 @@ bool LLTextureFetchWorker::doWork(S32 param) const S32 HTTP_MAX_RETRY_COUNT = 3; max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; + mFetcher->adjustHTTPConcurrency(false); llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; @@ -1869,7 +1870,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), - mOriginFetchSource(LLTextureFetch::FROM_ALL) + mOriginFetchSource(LLTextureFetch::FROM_ALL), + mHTTPConcurrency(24) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); @@ -2094,6 +2096,42 @@ S32 LLTextureFetch::getNumRequests() return size ; } +bool LLTextureFetch::canIssueHTTPRequest() +{ + LLMutexLock lock(&mNetworkQueueMutex); + + return (S32)mHTTPTextureQueue.size() < mHTTPConcurrency ; +} + +void LLTextureFetch::adjustHTTPConcurrency(bool success) +{ + static LLTimer timer; + + LLMutexLock lock(&mNetworkQueueMutex); + if(success) + { + if(mHTTPConcurrency < 21 && timer.getElapsedTimeF32() > 15.f) //seconds + { + mHTTPConcurrency += 4; //max is 24 + timer.reset(); + } + } + else + { + if(mHTTPConcurrency > 11 && timer.getElapsedTimeF32() > 2.0f) + { + mHTTPConcurrency -= 8; //min is 4 + timer.reset(); + } + } +} + +S32 LLTextureFetch::getHTTPConcurrency() +{ + LLMutexLock lock(&mNetworkQueueMutex); + return mHTTPConcurrency; +} + S32 LLTextureFetch::getNumHTTPRequests() { mNetworkQueueMutex.lock() ; @@ -2311,6 +2349,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) { mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. } + + adjustHTTPConcurrency(true); + return res; } @@ -3608,7 +3649,8 @@ S32 LLTextureFetchDebugger::fillCurlQueue() return 0; } - if (mNbCurlRequests == 24) + S32 max_concurrency = mFetcher->getHTTPConcurrency(); + if (mNbCurlRequests == max_concurrency) return mNbCurlRequests; S32 size = mFetchingHistory.size(); @@ -3630,7 +3672,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; mNbCurlRequests++; // Hack - if (mNbCurlRequests == 24) + if (mNbCurlRequests == max_concurrency) break; } else @@ -4007,6 +4049,8 @@ void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& ch mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; } + + mFetcher->adjustHTTPConcurrency(false); } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index d7677295c0..3ac08ecbc2 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -109,6 +109,10 @@ public: inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } + bool canIssueHTTPRequest(); + void adjustHTTPConcurrency(bool success); + S32 getHTTPConcurrency(); + protected: void addToNetworkQueue(LLTextureFetchWorker* worker); void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); @@ -209,6 +213,9 @@ private: // use the LLCurl module's request counter as it isn't thread compatible. // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. LLAtomic32 mCurlPOSTRequestCount; + + //control http concurrency for texture fetching + S32 mHTTPConcurrency; //which is adaptive to the network situation at an instant public: // A probabilistically-correct indicator that the current -- cgit v1.2.3 From 2716e38d42ca7c65a7017b465b499f752268d9e1 Mon Sep 17 00:00:00 2001 From: Jonathan Goodman Date: Thu, 10 May 2012 16:28:07 -0400 Subject: Encode viewspace Z normal to preserve negative Z (fixes all light shifting errors, and shiny environment map errors due to lack of negative Z) --- indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl | 2 +- .../newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl | 2 +- .../app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl | 2 +- .../app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl index 9a3b2e3e8a..e68af3c8b4 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl @@ -44,6 +44,6 @@ void main() gl_FragData[0] = vec4(diff.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 6cc5f23aca..ea8f32684d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -50,5 +50,5 @@ void main() gl_FragData[1] = vertex_color.aaaa; // spec //gl_FragData[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(tnorm); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl index e9989a4e48..04c3afaa97 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl @@ -47,6 +47,6 @@ void main() gl_FragData[0] = vec4(col.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); // spec vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl index fdf8d72b38..29502371bc 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl @@ -46,5 +46,5 @@ void main() gl_FragData[0] = vec4(col.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl index bb20e2ca47..5b3ab76a5f 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl @@ -47,6 +47,6 @@ void main() gl_FragData[0] = vec4(col.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); // spec vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 7bde49eb86..29b246c35f 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -40,6 +40,6 @@ void main() gl_FragData[1] = vertex_color.aaaa; // spec //gl_FragData[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl index 75b45111e0..30181a2ef3 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl @@ -39,5 +39,5 @@ void main() gl_FragData[1] = vertex_color.aaaa; // spec //gl_FragData[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 4c9ea24a24..07ed7b5401 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -159,5 +159,5 @@ void main() gl_FragData[0] = vec4(color.rgb, 0.5); // diffuse gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); // speccolor*spec, spec - gl_FragData[2] = vec4(screenspacewavef.xy*0.5+0.5, screenspacewavef.z, screenspacewavef.z*0.5); // normalxyz, displace + gl_FragData[2] = vec4(screenspacewavef.xyz*0.5+0.5, screenspacewavef.z*0.5); // normalxyz, displace } -- cgit v1.2.3 From a4eb314b76f7dfb490bed1868eed8af2315c48c8 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 10 May 2012 14:56:52 -0600 Subject: for SH-3132. roll back http concurrency for texture fetching back to 8. --- indra/newview/lltexturefetch.cpp | 33 ++------------------------------- indra/newview/lltexturefetch.h | 1 - 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index e96bcd2ad0..8ac6acf877 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1274,7 +1274,6 @@ bool LLTextureFetchWorker::doWork(S32 param) // requests instead of returning 503... we already limit the number pending. ++mHTTPFailCount; max_attempts = mHTTPFailCount+1; // Keep retrying - mFetcher->adjustHTTPConcurrency(false); LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } else @@ -1282,7 +1281,6 @@ bool LLTextureFetchWorker::doWork(S32 param) const S32 HTTP_MAX_RETRY_COUNT = 3; max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; - mFetcher->adjustHTTPConcurrency(false); llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; @@ -1871,7 +1869,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), mOriginFetchSource(LLTextureFetch::FROM_ALL), - mHTTPConcurrency(24) + mHTTPConcurrency(8) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); @@ -2103,32 +2101,9 @@ bool LLTextureFetch::canIssueHTTPRequest() return (S32)mHTTPTextureQueue.size() < mHTTPConcurrency ; } -void LLTextureFetch::adjustHTTPConcurrency(bool success) -{ - static LLTimer timer; - - LLMutexLock lock(&mNetworkQueueMutex); - if(success) - { - if(mHTTPConcurrency < 21 && timer.getElapsedTimeF32() > 15.f) //seconds - { - mHTTPConcurrency += 4; //max is 24 - timer.reset(); - } - } - else - { - if(mHTTPConcurrency > 11 && timer.getElapsedTimeF32() > 2.0f) - { - mHTTPConcurrency -= 8; //min is 4 - timer.reset(); - } - } -} - S32 LLTextureFetch::getHTTPConcurrency() { - LLMutexLock lock(&mNetworkQueueMutex); + //LLMutexLock lock(&mNetworkQueueMutex); return mHTTPConcurrency; } @@ -2350,8 +2325,6 @@ S32 LLTextureFetch::update(F32 max_time_ms) mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. } - adjustHTTPConcurrency(true); - return res; } @@ -4049,8 +4022,6 @@ void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& ch mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; } - - mFetcher->adjustHTTPConcurrency(false); } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 3ac08ecbc2..4d17cd0b7b 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -110,7 +110,6 @@ public: inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } bool canIssueHTTPRequest(); - void adjustHTTPConcurrency(bool success); S32 getHTTPConcurrency(); protected: -- cgit v1.2.3 From a4f27c1b5518cadff653d7e34cbe941d77b4f791 Mon Sep 17 00:00:00 2001 From: Jonathan Goodman Date: Thu, 10 May 2012 17:05:48 -0400 Subject: Unpack X, Y, and Z within the light shaders. Also some missing bits from terrainF and treeF. --- .../newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl | 2 +- indra/newview/app_settings/shaders/class1/deferred/treeF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index 179c721a2f..e59c6a1d93 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -77,7 +77,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); vec4 spec = texture2DRect(specularRect, frag.xy); vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl index b673d00d6e..85b7f885b5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl @@ -81,7 +81,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = dot(norm, lv); if (da < 0.0) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 0c53a4ffa5..c39ceddee6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -275,7 +275,7 @@ void main() float depth = texture2DRect(depthMap, tc.xy).r; vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = max(dot(norm.xyz, sun_dir.xyz), 0.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl index fc5959a33c..29fd59a9e5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl @@ -126,7 +126,7 @@ void main() vec4 pos = getPosition(pos_screen); vec3 norm = texture2DRect(normalMap, pos_screen).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm gl_FragColor[0] = 1.0; gl_FragColor[1] = calcAmbientOcclusion(pos, norm); diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl index 5522e6c41d..3a3b17d7f3 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl @@ -54,6 +54,6 @@ void main() gl_FragData[0] = vec4(outColor.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl index ea98d6884c..805d118f35 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl @@ -46,5 +46,5 @@ void main() gl_FragData[0] = vec4(vertex_color.rgb*col.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); vec3 nvn = normalize(vary_normal); - gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); + gl_FragData[2] = vec4(nvn.xyz * 0.5 + 0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index 14a683971a..8bfa44f2c5 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -152,7 +152,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 27ea77b5a2..6f696e2ed7 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -277,7 +277,7 @@ void main() float depth = texture2DRect(depthMap, tc.xy).r; vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm float da = max(dot(norm.xyz, sun_dir.xyz), 0.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl index 31bd0c79da..8791ef35f3 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl @@ -104,7 +104,7 @@ void main() } vec3 norm = texture2DRect(normalMap, frag.xy).xyz; - norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = (norm.xyz-0.5)*2.0; // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 229c2f4b67..56d6d60aa4 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -123,7 +123,7 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); - nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + nmap4 = vec4((nmap4.xyz-0.5)*2.0,nmap4.w); // unpack norm float displace = nmap4.w; vec3 norm = nmap4.xyz; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index 6b420833b9..54a51a4e16 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -184,7 +184,7 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); - nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + nmap4 = vec4((nmap4.xyz-0.5)*2.0,nmap4.w); // unpack norm float displace = nmap4.w; vec3 norm = nmap4.xyz; -- cgit v1.2.3 From b1d32e5d2deea546a0a2b9a4e1bb649928d3276f Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Mon, 14 May 2012 18:41:14 -0700 Subject: SH-3138 : Points to the new KDU 7.0 lib for Mac --- autobuild.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 045bf54a0f..7dc9107754 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -966,9 +966,9 @@ archive hash - dd87a5ffe89b01e6f60a4b9bb13baf99 + 48013e4f7ccc0a1daa224885e0221d29 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/250131/arch/Darwin/installer/kdu-7.0.0-darwin-20120227.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256864/arch/Darwin/installer/kdu-7.0.0-darwin-20120514.tar.bz2 name darwin -- cgit v1.2.3 From ab954444154de43ee18575a3b0649d0f3045dfd8 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Tue, 15 May 2012 13:45:19 -0700 Subject: SH-3138 : Update both Mac and Windows KDU 7.0 libs to the same build number for clarity --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 7dc9107754..713662b732 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -966,9 +966,9 @@ archive hash - 48013e4f7ccc0a1daa224885e0221d29 + a5b2dff0d97b643227a58473e5c57906 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256864/arch/Darwin/installer/kdu-7.0.0-darwin-20120514.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256978/arch/Darwin/installer/kdu-7.0.0-darwin-20120515.tar.bz2 name darwin @@ -990,9 +990,9 @@ archive hash - 0516ba3cf240d9e0b8e134c75a64de7a + 6d80d35524e1c0c32d3385014d02d48c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/250131/arch/CYGWIN/installer/kdu-7.0.0-windows-20120227.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/3p-kdu-private/rev/256978/arch/CYGWIN/installer/kdu-7.0.0-windows-20120515.tar.bz2 name windows -- cgit v1.2.3 From bfb02fa872805ab748d96389ca22cb0d05546161 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 17 May 2012 14:48:45 -0700 Subject: SH-3134 : Fix the http callback clearing the url so that further requests will look at the local cache first --- indra/newview/lltexturefetch.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 8ac6acf877..d5ecda1e61 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1739,6 +1739,10 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, { mRequestedSize = -1; // error } + // Clear the url since we're done with the fetch + // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch + // next time the texture is requested, even if the data have already been fetched. + mUrl.clear(); mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -- cgit v1.2.3 From 8fc350125c671baeae6b7f8b1814251009f4f50a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 23 May 2012 19:12:09 -0400 Subject: Integrate llcorehttp library into lltexturefetch design. This is the first functional viewer pass with the HTTP work of the texture fetch code performed by the llcorehttp library. Not exactly a 'drop-in' replacement but a work-alike with some changes (e.g. handler notification in consumer thread versus responder notification in worker thread). This also includes some temporary changes in the priority scheme to prevent the kind of priority inversion found in VWR-28996. Scheme used here does provide liveness if not optimal responsiveness or order-of-operation. The llcorehttp library at this point is far from optimally performing. Its worker thread is making relatively poor use of cycles it gets and it doesn't idle or sleep intelligently yet. This early integration step helps shake out the interfaces, implementation niceties will be covered soon. --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/_httplibcurl.cpp | 11 +- indra/llcorehttp/_httplibcurl.h | 3 +- indra/llcorehttp/_httpopcancel.cpp | 82 ++++ indra/llcorehttp/_httpopcancel.h | 75 ++++ indra/llcorehttp/_httpoperation.h | 25 -- indra/llcorehttp/_httpoprequest.cpp | 105 +++-- indra/llcorehttp/_httpoprequest.h | 9 +- indra/llcorehttp/_httppolicy.h | 3 +- indra/llcorehttp/_httpreplyqueue.h | 2 +- indra/llcorehttp/_httprequestqueue.h | 2 +- indra/llcorehttp/_httpservice.h | 4 +- indra/llcorehttp/httpcommon.cpp | 90 ++++- indra/llcorehttp/httpcommon.h | 59 ++- indra/llcorehttp/httprequest.cpp | 17 + indra/llcorehttp/httpresponse.cpp | 3 +- indra/llcorehttp/httpresponse.h | 32 +- indra/llcorehttp/tests/test_httpstatus.hpp | 102 +++++ indra/newview/llappviewer.cpp | 159 ++++++++ indra/newview/lltexturefetch.cpp | 610 +++++++++++++++++------------ indra/newview/lltexturefetch.h | 19 +- 21 files changed, 1057 insertions(+), 357 deletions(-) create mode 100644 indra/llcorehttp/_httpopcancel.cpp create mode 100644 indra/llcorehttp/_httpopcancel.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 81c502b642..ae92fb96fd 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -30,6 +30,7 @@ set(llcorehttp_SOURCE_FILES _httprequestqueue.cpp _httpoperation.cpp _httpoprequest.cpp + _httpopcancel.cpp _httpreplyqueue.cpp _httppolicy.cpp _httplibcurl.cpp @@ -49,6 +50,7 @@ set(llcorehttp_HEADER_FILES httpresponse.h _httpoperation.h _httpoprequest.h + _httpopcancel.h _httprequestqueue.h _httpreplyqueue.h _httpservice.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 15be977adf..1b951818e4 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -27,7 +27,6 @@ #include "_httplibcurl.h" #include "httpheaders.h" - #include "_httpoprequest.h" #include "_httpservice.h" @@ -173,8 +172,11 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode { int http_status(200); - curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); - op->mReplyStatus = http_status; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); + op->mStatus = LLCore::HttpStatus(http_status, + (http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR)); } // Detach from multi and recycle handle @@ -202,7 +204,8 @@ int HttpLibcurl::activeCount() const struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) { for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); - headers->mHeaders.end() != it; + + headers->mHeaders.end() != it; ++it) { slist = curl_slist_append(slist, (*it).c_str()); diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 01c68320af..807196628d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -45,6 +45,7 @@ class HttpOpRequest; class HttpHeaders; +/// Implements libcurl-based transport for an HttpService instance. class HttpLibcurl { public: @@ -71,7 +72,7 @@ protected: typedef std::set active_set_t; protected: - HttpService * mService; + HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; CURLM * mMultiHandles[1]; }; // end class HttpLibcurl diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp new file mode 100644 index 0000000000..69dbff4bb4 --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -0,0 +1,82 @@ +/** + * @file _httpopcancel.cpp + * @brief Definitions for internal class HttpOpCancel + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopcancel.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpCancel +// ================================== + + +HttpOpCancel::HttpOpCancel(HttpHandle handle) + : HttpOperation(), + mHandle(handle) +{} + + +HttpOpCancel::~HttpOpCancel() +{} + + +void HttpOpCancel::stageFromRequest(HttpService * service) +{ + // *FIXME: Need cancel functionality into services + addAsReply(); +} + + +void HttpOpCancel::visitNotifier(HttpRequest * request) +{ + if (mLibraryHandler) + { + HttpResponse * response = new HttpResponse(); + mLibraryHandler->onCompleted(static_cast(this), response); + response->release(); + } +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h new file mode 100644 index 0000000000..38ccc585ed --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.h @@ -0,0 +1,75 @@ +/** + * @file _httpopcancel.h + * @brief Internal declarations for the HttpOpCancel subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPCANCEL_H_ +#define _LLCORE_HTTP_OPCANCEL_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. Requests that have been made +/// active and are available for sending on the wire cannot +/// be canceled. + +class HttpOpCancel : public HttpOperation +{ +public: + HttpOpCancel(HttpHandle handle); + virtual ~HttpOpCancel(); + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + // Request data + HttpHandle mHandle; + +}; // end class HttpOpCancel + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPCANCEL_H_ + diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index d04961c47b..5d06a28586 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -87,31 +87,6 @@ public: }; // end class HttpOperation -/// HttpOpCancel requests that a previously issued request -/// be canceled, if possible. Requests that have been made -/// active and are available for sending on the wire cannot -/// be canceled. - -class HttpOpCancel : public HttpOperation -{ -public: - HttpOpCancel(); - virtual ~HttpOpCancel(); - -private: - HttpOpCancel(const HttpOpCancel &); // Not defined - void operator=(const HttpOpCancel &); // Not defined - -public: - virtual void stageFromRequest(HttpService *); - virtual void stageFromReady(HttpService *); - virtual void stageFromActive(HttpService *); - -public: - HttpHandle mHandle; -}; // end class HttpOpCancel - - /// HttpOpStop requests the servicing thread to shutdown /// operations, cease pulling requests from the request /// queue and release shared resources (particularly diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 3c9eb71b9a..521bd5b879 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -86,53 +86,53 @@ HttpOpRequest::HttpOpRequest() mReqMethod(HOR_GET), mReqBody(NULL), mReqOffset(0), - mReqLen(0), + mReqLength(0), mReqHeaders(NULL), mReqOptions(NULL), mCurlActive(false), mCurlHandle(NULL), - mCurlHeaders(NULL), mCurlService(NULL), - mReplyStatus(200), + mCurlHeaders(NULL), mReplyBody(NULL), mReplyOffset(0), - mReplyLen(0), + mReplyLength(0), mReplyHeaders(NULL) {} HttpOpRequest::~HttpOpRequest() { - if (mCurlHandle) - { - curl_easy_cleanup(mCurlHandle); - mCurlHandle = NULL; - } - - if (mCurlHeaders) - { - curl_slist_free_all(mCurlHeaders); - mCurlHeaders = NULL; - } - - mCurlService = NULL; - if (mReqBody) { mReqBody->release(); mReqBody = NULL; } + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + if (mReqHeaders) { - curl_slist_free_all(mReqHeaders); + mReqHeaders->release(); mReqHeaders = NULL; } - if (mReqOptions) + if (mCurlHandle) { - mReqOptions->release(); - mReqOptions = NULL; + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + mCurlService = NULL; + + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; } if (mReplyBody) @@ -165,28 +165,29 @@ void HttpOpRequest::stageFromReady(HttpService * service) void HttpOpRequest::stageFromActive(HttpService * service) { - if (mReplyLen) + if (mReplyLength) { // If non-zero, we received and processed a Content-Range // header with the response. Verify that what it says // is consistent with the received data. - if (mReplyLen != mReplyBody->size()) + if (mReplyLength != mReplyBody->size()) { // Not as expected, fail the request mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); } } - if (mReqHeaders) + if (mCurlHeaders) { // We take these headers out of the request now as they were // allocated originally in this thread and the notifier doesn't // need them. This eliminates one source of heap moving across // threads. - curl_slist_free_all(mReqHeaders); - mReqHeaders = NULL; + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; } + addAsReply(); } @@ -196,12 +197,24 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) if (mLibraryHandler) { HttpResponse * response = new HttpResponse(); - - // *FIXME: add http status, offset, length response->setStatus(mStatus); - response->setReplyStatus(mReplyStatus); response->setBody(mReplyBody); response->setHeaders(mReplyHeaders); + unsigned int offset(0), length(0); + if (mReplyOffset || mReplyLength) + { + // Got an explicit offset/length in response + offset = mReplyOffset; + length = mReplyLength; + } + else if (mReplyBody) + { + // Provide implicit offset/length from request/response + offset = mReqOffset; + length = mReplyBody->size(); + } + response->setRange(offset, length); + mLibraryHandler->onCompleted(static_cast(this), response); response->release(); @@ -235,14 +248,15 @@ HttpStatus HttpOpRequest::setupGetByteRange(unsigned int policy_id, mReqMethod = HOR_GET; mReqURL = url; mReqOffset = offset; - mReqLen = len; + mReqLength = len; if (offset || len) { mProcFlags |= PF_SCAN_RANGE_HEADER; } if (headers && ! mReqHeaders) { - mReqHeaders = append_headers_to_slist(headers, mReqHeaders); + headers->addRef(); + mReqHeaders = headers; } if (options && ! mReqOptions) { @@ -257,7 +271,7 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) { // *FIXME: better error handling later HttpStatus status; - + mCurlHandle = curl_easy_init(); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); @@ -268,14 +282,18 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); // curl_easy_setopt(handle, CURLOPT_PROXY, ""); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); - curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); - if (mReqOffset || mReqLen) + if (mReqHeaders) + { + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + if (mReqOffset || mReqLength) { static const char * fmt1("Range: bytes=%d-%d"); static const char * fmt2("Range: bytes=%d-"); @@ -284,16 +302,17 @@ HttpStatus HttpOpRequest::prepareForGet(HttpService * service) #if defined(WIN32) _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, - (mReqLen ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLen - 1); + (mReqLength ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLength - 1); #else snprintf(range_line, sizeof(range_line), - (mReqLen ? fmt1 : fmt2), - mReqOffset, mReqOffset + mReqLen - 1); + (mReqLength ? fmt1 : fmt2), + mReqOffset, mReqOffset + mReqLength - 1); #endif // defined(WIN32) range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { @@ -347,8 +366,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) { // One of possibly several status lines. Reset what we know and start over + // taking results from the last header stanza we receive. op->mReplyOffset = 0; - op->mReplyLen = 0; + op->mReplyLength = 0; op->mStatus = HttpStatus(); } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) @@ -371,7 +391,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi { // Success, record the fragment position op->mReplyOffset = first; - op->mReplyLen = last - first + 1; + op->mReplyLength = last - first + 1; } else if (-1 == status) { @@ -390,6 +410,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (op->mProcFlags & PF_SAVE_HEADERS) { // Save headers in response + // *FIXME: Implement this... ; } diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 232ee841d6..601937a943 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -103,22 +103,21 @@ public: std::string mReqURL; BufferArray * mReqBody; off_t mReqOffset; - size_t mReqLen; - curl_slist * mReqHeaders; + size_t mReqLength; + HttpHeaders * mReqHeaders; HttpOptions * mReqOptions; // Transport data bool mCurlActive; CURL * mCurlHandle; - curl_slist * mCurlHeaders; HttpService * mCurlService; + curl_slist * mCurlHeaders; // Result data HttpStatus mStatus; - int mReplyStatus; BufferArray * mReplyBody; off_t mReplyOffset; - size_t mReplyLen; + size_t mReplyLength; HttpHeaders * mReplyHeaders; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 28aea27f38..192bc73b31 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -39,6 +39,7 @@ class HttpService; class HttpOpRequest; +/// Implements class-based queuing policies for an HttpService instance. class HttpPolicy { public: @@ -58,7 +59,7 @@ protected: typedef std::vector ready_queue_t; protected: - HttpService * mService; // Naked pointer, not refcounted + HttpService * mService; // Naked pointer, not refcounted, not owner ready_queue_t mReadyQueue; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 56dadec87c..28cb1d68b7 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -58,7 +58,7 @@ class HttpOperation; /// will be coded anyway so it shouldn't be too much of a /// burden. -class HttpReplyQueue: public LLCoreInt::RefCounted +class HttpReplyQueue : public LLCoreInt::RefCounted { public: /// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 3a9ce0c3c6..f96bd7520c 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -46,7 +46,7 @@ class HttpOperation; /// requests from all HttpRequest instances into the /// singleton HttpService instance. -class HttpRequestQueue: public LLCoreInt::RefCounted +class HttpRequestQueue : public LLCoreInt::RefCounted { protected: /// Caller acquires a Refcount on construction diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index c052e35452..ba76e1eeca 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -152,8 +152,8 @@ protected: LLCoreInt::HttpThread * mThread; // === working-thread-only data === - HttpPolicy * mPolicy; - HttpLibcurl * mTransport; + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership }; // end class HttpService diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index c37d081150..b5872606b8 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -37,22 +37,82 @@ HttpStatus::type_enum_t EXT_CURL_EASY; HttpStatus::type_enum_t EXT_CURL_MULTI; HttpStatus::type_enum_t LLCORE; +HttpStatus::operator unsigned long() const +{ + static const int shift(sizeof(unsigned long) * 4); + + unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + return result; +} + + std::string HttpStatus::toString() const { static const char * llcore_errors[] = { "", + "HTTP error reply status", "Services shutting down", "Operation canceled", "Invalid Content-Range header encountered" }; static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + static const struct + { + type_enum_t mCode; + char * mText; + } + http_errors[] = + { + // Keep sorted by mCode, we binary search this list. + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Large" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Time-out" }, + { 505, "HTTP Version not supported" } + }; + static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0])); if (*this) { return std::string(""); } - switch (mType) { case EXT_CURL_EASY: @@ -67,7 +127,33 @@ std::string HttpStatus::toString() const return std::string(llcore_errors[mStatus]); } break; - + + default: + if (isHttpStatus()) + { + int bottom(0), top(http_errors_count); + while (true) + { + int at((bottom + top) / 2); + if (mType == http_errors[at].mCode) + { + return std::string(http_errors[at].mText); + } + if (at == bottom) + { + break; + } + else if (mType < http_errors[at].mCode) + { + top = at; + } + else + { + bottom = at; + } + } + } + break; } return std::string("Unknown error"); } diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 9de5769d57..f81be7103e 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -122,23 +122,38 @@ enum HttpError // Successful value compatible with the libcurl codes. HE_SUCCESS = 0, + // Intended for HTTP reply codes 100-999, indicates that + // the reply should be considered an error by the application. + HE_REPLY_ERROR = 1, + // Service is shutting down and requested operation will // not be queued or performed. - HE_SHUTTING_DOWN = 1, + HE_SHUTTING_DOWN = 2, // Operation was canceled by request. - HE_OP_CANCELED = 2, + HE_OP_CANCELED = 3, // Invalid content range header received. - HE_INV_CONTENT_RANGE_HDR = 3 + HE_INV_CONTENT_RANGE_HDR = 4 }; // end enum HttpError -/// HttpStatus encapsulates errors from libcurl (easy, multi) as well as -/// internal errors. The encapsulation isn't expected to completely -/// isolate the caller from libcurl but basic operational tests (success -/// or failure) are provided. +/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP +/// reply status codes and internal errors as well. The encapsulation +/// isn't expected to completely isolate the caller from libcurl but +/// basic operational tests (success or failure) are provided. +/// +/// Non-HTTP status are encoded as (type, status) with type being +/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status +/// being the success/error code from that domain. HTTP status +/// is encoded as (status, error_flag). Status should be in the +/// range [100, 999] and error_flag is either HE_SUCCESS or +/// HE_REPLY_ERROR to indicate whether this should be treated as +/// a successful status or an error. The application is responsible +/// for making that determination and a range like [200, 299] isn't +/// automatically assumed to be definitive. + struct HttpStatus { typedef unsigned short type_enum_t; @@ -192,12 +207,42 @@ struct HttpStatus return 0 != mStatus; } + /// Equality and inequality tests to bypass bool conversion + /// which will do the wrong thing in conditional expressions. + bool operator==(const HttpStatus & rhs) const + { + return mType == rhs.mType && mStatus == rhs.mStatus; + } + + bool operator!=(const HttpStatus & rhs) const + { + return ! operator==(rhs); + } + + /// Convert to single numeric representation. Mainly + /// for logging or other informal purposes. Also + /// creates an ambiguous second path to integer conversion + /// which tends to find programming errors such as formatting + /// the status to a stream (operator<<). + operator unsigned long() const; + unsigned long toULong() const + { + return operator unsigned long(); + } + /// Convert status to a string representation. For /// success, returns an empty string. For failure /// statuses, a string as appropriate for the source of /// the error code (libcurl easy, libcurl multi, or /// LLCore itself). std::string toString() const; + + /// Returns true if the status value represents an + /// HTTP response status (100 - 999). + bool isHttpStatus() const + { + return mType >= type_enum_t(100) && mType <= type_enum_t(999); + } }; // end struct HttpStatus diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2a87f5231a..6c62f931ff 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -31,6 +31,7 @@ #include "_httpservice.h" #include "_httpoperation.h" #include "_httpoprequest.h" +#include "_httpopcancel.h" namespace @@ -189,6 +190,22 @@ HttpHandle HttpRequest::requestGetByteRange(unsigned int policy_id, } +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(handle); + op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) { HttpStatus status; diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index 9ac8276f05..3dcdadb337 100644 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -35,7 +35,8 @@ namespace LLCore HttpResponse::HttpResponse() : LLCoreInt::RefCounted(true), - mReplyStatus(0U), + mReplyOffset(0U), + mReplyLength(0U), mBufferArray(NULL), mHeaders(NULL) {} diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index a25e22aef6..5cf3a919f4 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -67,8 +67,6 @@ protected: public: /// Returns the final status of the requested operation. /// - // *FIXME: Haven't incorporated HTTP status into this yet. - // Will do soon. HttpStatus getStatus() const { return mStatus; @@ -79,19 +77,6 @@ public: mStatus = status; } - /// Fetch the HTTP reply status. This is only guaranteed to be - /// valid if the HttpStatus tests successful and was the result - /// of a completed HTTP request. - unsigned int getReplyStatus() const - { - return mReplyStatus; - } - - void setReplyStatus(unsigned int status) - { - mReplyStatus = status; - } - /// Simple getter for the response body returned as a scatter/gather /// buffer. If the operation doesn't produce data (such as the Null /// or StopThread operations), this may be NULL. @@ -122,11 +107,26 @@ public: /// Behaves like @see setResponse() but for header data. void setHeaders(HttpHeaders * headers); + + /// If a 'Range:' header was used, these methods are involved + /// in setting and returning data about the actual response. + void getRange(unsigned int * offset, unsigned int * length) const + { + *offset = mReplyOffset; + *length = mReplyLength; + } + + void setRange(unsigned int offset, unsigned int length) + { + mReplyOffset = offset; + mReplyLength = length; + } protected: // Response data here HttpStatus mStatus; - unsigned int mReplyStatus; + unsigned int mReplyOffset; + unsigned int mReplyLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; }; diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp index 38bf494dec..f7b542d3b5 100644 --- a/indra/llcorehttp/tests/test_httpstatus.hpp +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -157,6 +157,108 @@ void HttpStatusTestObjectType::test<4>() ensure(! msg.empty()); } +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ + set_test_name("HttpStatus equality/inequality testing"); + + // Make certain equality/inequality tests do not pass + // through the bool conversion. Distinct successful + // and error statuses should compare unequal. + + HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); + HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); + ensure(status1 != status2); + + status1.mType = HttpStatus::LLCORE; + status1.mStatus = HE_REPLY_ERROR; + status2.mType = HttpStatus::LLCORE; + status2.mStatus= HE_SHUTTING_DOWN; + ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ + set_test_name("HttpStatus basic HTTP status encoding"); + + HttpStatus status; + status.mType = 200; + status.mStatus = HE_SUCCESS; + std::string msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Normally a success but application says error + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(! msg.empty()); + ensure(! bool(status)); + ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong + + // Same statuses with distinct success/fail are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + HttpStatus status2(200, HE_REPLY_ERROR); + ensure(status != status2); + + // Normally an error but application says okay + status.mType = 406; + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Different statuses but both successful are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + status2.mType = 201; + status2.mStatus = HE_SUCCESS; + ensure(status != status2); + + // Different statuses but both failed are distinct + status.mType = 200; + status.mStatus = HE_REPLY_ERROR; + status2.mType = 201; + status2.mStatus = HE_REPLY_ERROR; + ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ + set_test_name("HttpStatus HTTP error text strings"); + + HttpStatus status(100, HE_REPLY_ERROR); + std::string msg(status.toString()); + ensure(! msg.empty()); // Should be something + ensure(msg == "Continue"); + + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); // Success is empty + + status.mType = 199; + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 505; // Last defined string + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "HTTP Version not supported"); + + status.mType = 506; // One beyond + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 999; // Last HTTP status + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); +} + } // end namespace tut #endif // TEST_HTTP_STATUS_H diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1174d108d2..8e6deb9cce 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -212,6 +212,11 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" +// LLCore::HTTP +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -326,6 +331,53 @@ static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; +namespace +{ + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class CoreHttp : public LLCore::HttpHandler +{ +public: + CoreHttp(); + ~CoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; +}; + +CoreHttp coreHttpLib; + +} // end anonymous namespace + //-- LLDeferredTaskList ------------------------------------------------------ /** @@ -720,6 +772,9 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library + coreHttpLib.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1807,6 +1862,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + coreHttpLib.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1890,6 +1946,9 @@ bool LLAppViewer::cleanup() // *NOTE:Mani - The following call is not thread safe. LLCurl::cleanupClass(); + // Non-LLCurl libcurl library + coreHttpLib.cleanup(); + // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. if (!gLaunchFileOnQuit.empty()) @@ -5267,3 +5326,103 @@ void LLAppViewer::metricsSend(bool enable_reporting) gViewerAssetStatsMain->reset(); } +namespace +{ + +const F64 CoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +CoreHttp::CoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false) +{} + + +CoreHttp::~CoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void CoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void CoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void CoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void CoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} + +} // end anonymous namespace diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f18aa8b4e6..17c68f7c22 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -33,7 +33,6 @@ #include "lltexturefetch.h" -#include "llcurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -53,11 +52,17 @@ #include "llviewerassetstats.h" #include "llworld.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" + + ////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass +class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler + { friend class LLTextureFetch; - friend class HTTPGetResponder; private: class CacheReadResponder : public LLTextureCache::ReadResponder @@ -147,15 +152,14 @@ public: ~LLTextureFetchWorker(); // void relese() { --mActiveCount; } - S32 callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(bool success); void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); - void setGetStatus(U32 status, const std::string& reason) + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -167,6 +171,9 @@ public: bool getCanUseHTTP() const { return mCanUseHTTP; } LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, @@ -191,9 +198,15 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + void recordTextureStart(bool is_http); + void recordTextureDone(bool is_http); + private: enum e_state // mState { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) INVALID = 0, INIT, @@ -244,9 +257,8 @@ private: LLFrameTimer mFetchTimer; LLTextureCache::handle_t mCacheReadHandle; LLTextureCache::handle_t mCacheWriteHandle; - U8* mBuffer; - S32 mBufferSize; S32 mRequestedSize; + S32 mRequestedOffset; S32 mDesiredSize; S32 mFileSize; S32 mCachedSize; @@ -263,7 +275,7 @@ private: S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; - U32 mGetStatus; + LLCore::HttpStatus mGetStatus; std::string mGetReason; // Work Data @@ -283,106 +295,17 @@ private: U8 mImageCodec; LLViewerAssetStats::duration_t mMetricsStartTime; + + LLCore::HttpHandle mHttpHandle; + LLCore::BufferArray * mHttpBufferArray; + int mHttpPolicyClass; + bool mHttpActive; + unsigned int mHttpReplySize; + unsigned int mHttpReplyOffset; }; ////////////////////////////////////////////////////////////////////////////// -class HTTPGetResponder : public LLCurl::Responder -{ - LOG_CLASS(HTTPGetResponder); -public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) - : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) - { - } - ~HTTPGetResponder() - { - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); - static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - lldebugs << "HTTP COMPLETE: " << mID << llendl; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - - if (!success) - { - worker->setGetStatus(status, reason); -// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; - } - - S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); - - if(log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; - if(tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - - mFetcher->removeFromHTTPQueue(mID, data_size); - - if (worker->mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType, - LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); - worker->mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType); - } - else - { - mFetcher->removeFromHTTPQueue(mID); - llwarns << "Worker not found: " << mID << llendl; - } - } - - virtual bool followRedir() - { - return mFollowRedir; - } - -private: - LLTextureFetch* mFetcher; - LLUUID mID; - U64 mStartTime; - S32 mRequestedSize; - U32 mOffset; - bool mFollowRedir; -}; ////////////////////////////////////////////////////////////////////////////// @@ -639,6 +562,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, S32 discard, // Desired discard S32 size) // Desired size : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), mState(INIT), mWriteToCacheState(NOT_WRITE), mFetcher(fetcher), @@ -655,9 +579,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mDecodedDiscard(-1), mCacheReadHandle(LLTextureCache::nullHandle()), mCacheWriteHandle(LLTextureCache::nullHandle()), - mBuffer(NULL), - mBufferSize(0), mRequestedSize(0), + mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), @@ -673,13 +596,18 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), - mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), - mMetricsStartTime(0) + mMetricsStartTime(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U) { mCanUseNET = mUrl.empty() ; @@ -701,6 +629,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); + if (mHttpActive) + { + LL_WARNS("Texture") << "Deleting worker object while HTTP request is active." + << LL_ENDL; + } lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { @@ -714,6 +647,11 @@ LLTextureFetchWorker::~LLTextureFetchWorker() clearPackets(); unlockWorkMutex(); mFetcher->removeFromHTTPQueue(mID); + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } } void LLTextureFetchWorker::clearPackets() @@ -797,7 +735,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) if ((prioritize && mState == INIT) || mState == DONE) { mState = INIT; - U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; + U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; setPriority(work_priority); } } @@ -810,16 +748,18 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) { mImagePriority = priority; calcWorkPriority(); - U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); + U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_LOW; setPriority(work_priority); } } void LLTextureFetchWorker::resetFormattedData() { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } if (mFormattedImage.notNull()) { mFormattedImage->deleteData(); @@ -875,6 +815,14 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetchTimer.reset(); } + static LLUUID last_id; + if (mID != last_id) + { + // LL_WARNS("Texture") << "DOWORK SWITCH: " << last_id << " to: " << mID + // << LL_ENDL; + last_id = mID; + } + if (mState == INIT) { mRawImage = NULL ; @@ -882,15 +830,18 @@ bool LLTextureFetchWorker::doWork(S32 param) mLoadedDiscard = -1; mDecodedDiscard = -1; mRequestedSize = 0; + mRequestedOffset = 0; mFileSize = 0; mCachedSize = 0; mLoaded = FALSE; mSentRequest = UNSENT; mDecoded = FALSE; mWritten = FALSE; - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); @@ -904,6 +855,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_TEXTURE_CACHE) { + setPriority(0); // Set priority first since Responder may change it if (mCacheReadHandle == LLTextureCache::nullHandle()) { U32 cache_priority = mWorkPriority; @@ -919,8 +871,6 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); @@ -929,8 +879,6 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -942,12 +890,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // *TODO:?remove this warning llwarns << "Unknown URL Type: " << mUrl << llendl; } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = SEND_HTTP_REQ; } else { - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = LOAD_FROM_NETWORK; } } @@ -959,6 +907,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mCacheReadHandle = LLTextureCache::nullHandle(); mState = CACHE_POST; + setPriority(LLWorkerThread::PRIORITY_HIGH); // fall through } else @@ -982,6 +931,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mFormattedImage->getDataSize() > 0); mLoadedDiscard = mDesiredDiscard; mState = DECODE_IMAGE; + setPriority(LLWorkerThread::PRIORITY_HIGH); mWriteToCacheState = NOT_WRITE ; LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) @@ -999,6 +949,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = LOAD_FROM_NETWORK; } // fall through @@ -1009,6 +960,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl use_http(gSavedSettings,"ImagePipelineUseHTTP"); + setPriority(0); // if (mHost != LLHost::invalid) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { @@ -1040,8 +992,8 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = LLTextureFetchWorker::SEND_HTTP_REQ; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = SEND_HTTP_REQ; + setPriority(LLWorkerThread::PRIORITY_HIGH); if(mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = CAN_WRITE ; @@ -1057,14 +1009,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); return false; } @@ -1074,12 +1019,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); - //if (! mMetricsStartTime) - //{ - // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - //} - //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, - // LLImageBase::TYPE_AVATAR_BAKE == mType); + //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1087,6 +1027,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == LOAD_FROM_SIMULATOR) { + setPriority(0); if (mFormattedImage.isNull()) { mFormattedImage = new LLImageJ2C; @@ -1101,39 +1042,22 @@ bool LLTextureFetchWorker::doWork(S32 param) // llwarns << "processSimulatorPackets() failed to load buffer" << llendl; return true; // failed } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; - - if (mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); - mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureDone(false); } else { mFetcher->addToNetworkQueue(this); // failsafe - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); } return false; } if (mState == SEND_HTTP_REQ) { + setPriority(0); if(mCanUseHTTP) { //NOTE: @@ -1159,6 +1083,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } @@ -1171,44 +1096,48 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedSize = mDesiredSize; mRequestedDiscard = mDesiredDiscard; mRequestedSize -= cur_size; - S32 offset = cur_size; - mBufferSize = cur_size; // This will get modified by callbackHttpGet() + mRequestedOffset = cur_size; - bool res = false; + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; if (!mUrl.empty()) { mLoaded = FALSE; - mGetStatus = 0; + mGetStatus = LLCore::HttpStatus(); mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset << " Bytes: " << mRequestedSize << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - mFetcher->addToHTTPQueue(mID); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == mType); +// LL_WARNS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset +// << " Bytes: " << mRequestedSize +// << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth +// << LL_ENDL; // Will call callbackHttpGet when curl request completes - std::vector headers; - headers.push_back("Accept: image/x-j2c"); - res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + // *FIXME: enable redirection follow + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mRequestedPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); } - if (!res) + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); ++mHTTPFailCount; return true; // failed } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_HIGH); + mState = WAIT_HTTP_REQ; + // fall through } else //can not use http fetch. @@ -1219,27 +1148,28 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_HTTP_REQ) { + setPriority(0); if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { S32 max_attempts; - if (mGetStatus == HTTP_NOT_FOUND) + if (mGetStatus == LLCore::HttpStatus(HTTP_NOT_FOUND, LLCore::HE_REPLY_ERROR)) { mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; //roll back to try UDP - if(mCanUseNET) + if (mCanUseNET) { - mState = INIT ; - mCanUseHTTP = false ; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - return false ; + mState = INIT; + mCanUseHTTP = false; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + return false; } } - else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) + else if (mGetStatus == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE, LLCore::HE_REPLY_ERROR)) { // *TODO: Should probably introduce a timer here to delay future HTTP requsts // for a short time (~1s) to ease server load? Ideally the server would queue @@ -1254,7 +1184,7 @@ bool LLTextureFetchWorker::doWork(S32 param) max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" + << " Status: " << mGetStatus.toULong() << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } @@ -1264,12 +1194,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; return false; } else { resetFormattedData(); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; // failed } @@ -1281,17 +1213,25 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - llassert_always(mBufferSize == cur_size + mRequestedSize); - if(!mBufferSize)//no data received. + if (! mHttpBufferArray || ! mHttpBufferArray->size()) { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } - //abort. + // abort. + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return true; } + const S32 append_size(mHttpBufferArray->size()); + const S32 total_size(cur_size + append_size); + llassert_always(append_size == mRequestedSize); + if (mFormattedImage.isNull()) { // For now, create formatted image based on extension @@ -1305,49 +1245,52 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. { - mFileSize = mBufferSize; + mFileSize = total_size; } else //the file size is unknown. { - mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. + mFileSize = total_size + 1 ; //flag the file is not fully loaded. } - U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize); + U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size); if (cur_size > 0) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append + mHttpBufferArray->seek(0); + mHttpBufferArray->read((char *) buffer + cur_size, append_size); + // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, mBufferSize); - // delete temp data - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData()) - mBuffer = NULL; - mBufferSize = 0; + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mLoadedDiscard = mRequestedDiscard; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DECODE_IMAGE; - if(mWriteToCacheState != NOT_WRITE) + if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } if (mState == DECODE_IMAGE) { + setPriority(0); static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - if(textures_decode_disabled) + if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1355,7 +1298,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } @@ -1365,7 +1308,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } if (mLoadedDiscard < 0) @@ -1374,10 +1317,10 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return true; } - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -1394,6 +1337,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE_UPDATE) { + setPriority(0); if (mDecoded) { if (mDecodedDiscard < 0) @@ -1406,13 +1350,14 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; ++mRetryAttempt; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = INIT; return false; } else { // llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // failed } } @@ -1421,7 +1366,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llassert_always(mRawImage.notNull()); LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = WRITE_TO_CACHE; } // fall through @@ -1434,10 +1379,12 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WRITE_TO_CACHE) { + setPriority(0); if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) { // If we're in a local cache or we didn't actually receive any new data, // or we failed to load anything, skip + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; return false; } @@ -1457,6 +1404,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = WAIT_ON_WRITE; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, @@ -1467,8 +1415,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == WAIT_ON_WRITE) { + setPriority(0); if (writeToCacheComplete()) { + setPriority(LLWorkerThread::PRIORITY_HIGH); mState = DONE; // fall through } @@ -1487,16 +1437,15 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } } @@ -1504,6 +1453,71 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toULong() << " '" << status.toString() << "'" + << llendl; + unsigned int offset(0), length(0); + response->getRange(&offset, &length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status:" << status.toULong() << " reason:" << reason << llendl; + } + else + { + static const LLCore::HttpStatus par_status(LLCore::HttpStatus(HTTP_PARTIAL_CONTENT, LLCore::HE_SUCCESS)); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID); + + recordTextureDone(true); +} + + // Called from MAIN thread void LLTextureFetchWorker::endWork(S32 param, bool aborted) { @@ -1537,6 +1551,14 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) bool LLTextureFetchWorker::deleteOK() { bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { @@ -1642,9 +1664,8 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) { S32 data_size = 0 ; @@ -1664,15 +1685,22 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, if (success) { // get length of stream: - data_size = buffer->countAfter(channels.in(), NULL); - + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, mBuffer, data_size); - mBufferSize += data_size; + // *FIXME: deal with actual offset and actual datasize, don't assume + // server gave exactly what was asked for. + + llassert_always(NULL == mHttpBufferArray); + + // Hold on to body for later copy + body->addRef(); + mHttpBufferArray = body; + if (data_size < mRequestedSize && mRequestedDiscard == 0) { mHaveAllData = TRUE; @@ -1684,7 +1712,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had - mBufferSize = data_size; } } else @@ -1700,7 +1727,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, mRequestedSize = -1; // error } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); return data_size ; } @@ -1729,7 +1756,7 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } } mLoaded = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } void LLTextureFetchWorker::callbackCacheWrite(bool success) @@ -1741,7 +1768,7 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) return; } mWritten = TRUE; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } ////////////////////////////////////////////////////////////////////////////// @@ -1779,7 +1806,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag } mDecoded = TRUE; // llinfos << mID << " : DECODE COMPLETE " << llendl; - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_HIGH); } ////////////////////////////////////////////////////////////////////////////// @@ -1806,6 +1833,34 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +void LLTextureFetchWorker::recordTextureStart(bool is_http) +{ + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + +void LLTextureFetchWorker::recordTextureDone(bool is_http) +{ + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // public @@ -1823,17 +1878,26 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureBandwidth(0), mHTTPTextureBits(0), mTotalHTTPRequests(0), - mCurlGetRequest(NULL), - mQAMode(qa_mode) + mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); } LLTextureFetch::~LLTextureFetch() { - clearDeleteList() ; + cancelHttpRequests(); + + clearDeleteList(); while (! mCommands.empty()) { @@ -1841,7 +1905,22 @@ LLTextureFetch::~LLTextureFetch() mCommands.erase(mCommands.begin()); delete req; } - + + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + + delete mHttpRequest; + mHttpRequest = NULL; + // ~LLQueuedThread() called here } @@ -1984,6 +2063,28 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits } +void LLTextureFetch::cancelHttpRequests() +{ + for (queue_t::iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + LLTextureFetchWorker* worker = getWorker(*iter); + if (worker && worker->mHttpActive) + { + mHttpRequest->requestCancel(worker->mHttpHandle, NULL); + } + } + + // *FIXME: Do this better with less time wasting. + int tries(10); + while (! mHTTPTextureQueue.empty() && --tries) + { + mHttpRequest->update(100); + ms_sleep(100); + } +} + void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { lockQueue() ; @@ -2194,11 +2295,21 @@ void LLTextureFetch::commonUpdate() cmdDoWork(); // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); + LLCore::HttpStatus status = mHttpRequest->update(200); + if (! status) + { + LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; + } + +#if 0 + // *FIXME: maybe implement this another way... if (processed > 0) { lldebugs << "processed: " << processed << " messages." << llendl; } +#endif } @@ -2256,22 +2367,22 @@ void LLTextureFetch::shutDownImageDecodeThread() // WORKER THREAD void LLTextureFetch::startThread() { - // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); } // WORKER THREAD +// +// This detaches the texture fetch thread from the LLCore +// HTTP library but doesn't stop the thread running in that +// library... void LLTextureFetch::endThread() { - // Destroy mCurlGetRequest from Worker Thread - delete mCurlGetRequest; - mCurlGetRequest = NULL; + cancelHttpRequests(); } // WORKER THREAD void LLTextureFetch::threadedUpdate() { - llassert_always(mCurlGetRequest); + llassert_always(mHttpRequest); // Limit update frequency const F32 PROCESS_TIME = 0.05f; @@ -2579,7 +2690,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 llassert_always(totalbytes > 0); llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); res = worker->insertPacket(0, data, data_size); - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; worker->unlockWorkMutex(); return res; @@ -2623,7 +2734,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) { - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->setPriority(LLWorkerThread::PRIORITY_HIGH); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; } else @@ -2730,6 +2841,14 @@ void LLTextureFetch::dump() << " STATE: " << worker->sStateDescs[worker->mState] << llendl; } + + llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } } ////////////////////////////////////////////////////////////////////////////// @@ -2942,6 +3061,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { LLCurlRequest::headers_t headers; +#if 0 + // *FIXME: Going to need a post op after all... fetcher->getCurlRequest().post(mCapsURL, headers, merged_llsd, @@ -2950,6 +3071,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) report_sequence, LLTextureFetch::svMetricsDataBreak, reporting_started)); +#endif } else { diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 35df7d816f..402b198246 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -31,9 +31,11 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" #include "lltextureinfo.h" #include "llapr.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" class LLViewerTexture; class LLTextureFetchWorker; @@ -98,7 +100,7 @@ public: LLViewerAssetStats * main_stats); void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } bool isQAMode() const { return mQAMode; } @@ -112,7 +114,8 @@ protected: void addToHTTPQueue(const LLUUID& id); void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); void removeRequest(LLTextureFetchWorker* worker, bool cancel); - + void cancelHttpRequests(); + // Overrides from the LLThread tree bool runCondition(); @@ -166,7 +169,6 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; @@ -203,6 +205,13 @@ private: // use the LLCurl module's request counter as it isn't thread compatible. // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. LLAtomic32 mCurlPOSTRequestCount; + + // Interfaces and objects into the core http library used + // to make our HTTP requests. These replace the various + // LLCurl interfaces used in the past. + LLCore::HttpRequest * mHttpRequest; + LLCore::HttpOptions * mHttpOptions; + LLCore::HttpHeaders * mHttpHeaders; public: // A probabilistically-correct indicator that the current -- cgit v1.2.3 From e37917b66856d8737eae378c2017c3525f756b4d Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 24 May 2012 14:32:14 -0600 Subject: fix for VWR-28996: Texture stalls and threading models --- indra/llmessage/llcurl.cpp | 168 ++++++++++++++++++++++++++++++++++++- indra/llmessage/llcurl.h | 61 ++++++++++++++ indra/newview/lltexturefetch.cpp | 175 +++++++++++++++++---------------------- indra/newview/lltexturefetch.h | 27 ++---- indra/newview/lltextureview.cpp | 4 +- 5 files changed, 311 insertions(+), 124 deletions(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index f153c94911..c06cf2489d 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -911,8 +911,8 @@ bool LLCurlThread::CurlRequest::processRequest() if(!completed) { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } + setPriority(LLQueuedThread::PRIORITY_LOW) ; + } } return completed ; @@ -1224,6 +1224,170 @@ S32 LLCurlRequest::getQueued() return queued; } +LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : + LLCurlRequest(), + mConcurrency(concurrency), + mInQueue(0), + mMutex(NULL), + mHandleCounter(1), + mTotalIssuedRequests(0), + mTotalReceivedBits(0) +{ +} + +LLCurlTextureRequest::~LLCurlTextureRequest() +{ + mRequestMap.clear(); + + for(req_queue_t::iterator iter = mCachedRequests.begin(); iter != mCachedRequests.end(); ++iter) + { + delete *iter; + } + mCachedRequests.clear(); +} + +//return 0: success +// > 0: cached handle +U32 LLCurlTextureRequest::getByteRange(const std::string& url, + const headers_t& headers, + S32 offset, S32 length, U32 pri, + LLCurl::ResponderPtr responder) +{ + U32 ret_val = 0; + bool success = false; + + if(mInQueue < mConcurrency) + { + success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); + } + + LLMutexLock lock(&mMutex); + + if(success) + { + mInQueue++; + mTotalIssuedRequests++; + } + else + { + request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); + mCachedRequests.insert(request); + mRequestMap[mHandleCounter] = request; + ret_val = mHandleCounter; + mHandleCounter++; + + if(!mHandleCounter) + { + mHandleCounter = 1; + } + } + + return ret_val; +} + +void LLCurlTextureRequest::completeRequest(S32 received_bytes) +{ + LLMutexLock lock(&mMutex); + + llassert_always(mInQueue > 0); + + mInQueue--; + mTotalReceivedBits += received_bytes * 8; +} + +void LLCurlTextureRequest::nextRequests() +{ + while(!mCachedRequests.empty() && mInQueue < mConcurrency) + { + request_t* request; + + { + LLMutexLock lock(&mMutex); + req_queue_t::iterator iter = mCachedRequests.begin(); + request = *iter; + } + + if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) + { + break; + } + + { + LLMutexLock lock(&mMutex); + mInQueue++; + mTotalIssuedRequests++; + llassert_always(1 == mCachedRequests.erase(request)); + mRequestMap.erase(request->mHandle); + } + delete request; + } + + return; +} + +void LLCurlTextureRequest::updatePriority(U32 handle, U32 pri) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + + if(req->mPriority != pri) + { + mCachedRequests.erase(req); + req->mPriority = pri; + mCachedRequests.insert(req); + } + } +} + +void LLCurlTextureRequest::removeRequest(U32 handle) +{ + if(!handle) + { + return; + } + + LLMutexLock lock(&mMutex); + + std::map::iterator iter = mRequestMap.find(handle); + if(iter != mRequestMap.end()) + { + request_t* req = iter->second; + mRequestMap.erase(iter); + mCachedRequests.erase(req); + delete req; + } +} + +U32 LLCurlTextureRequest::getTotalReceivedBits() +{ + LLMutexLock lock(&mMutex); + + U32 bits = mTotalReceivedBits; + mTotalReceivedBits = 0; + return bits; +} + +U32 LLCurlTextureRequest::getTotalIssuedRequests() +{ + LLMutexLock lock(&mMutex); + return mTotalIssuedRequests; +} + +S32 LLCurlTextureRequest::getNumRequests() +{ + LLMutexLock lock(&mMutex); + return mInQueue; +} + //////////////////////////////////////////////////////////////////////////// // For generating one easy request // associated with a single multi request diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index fd664c0fa1..04a2bd4287 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -413,6 +413,67 @@ private: BOOL mProcessing; }; +//for texture fetch only +class LLCurlTextureRequest : public LLCurlRequest +{ +public: + LLCurlTextureRequest(S32 concurrency); + ~LLCurlTextureRequest(); + + U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder); + void nextRequests(); + void completeRequest(S32 received_bytes); + + void updatePriority(U32 handle, U32 pri); + void removeRequest(U32 handle); + + U32 getTotalReceivedBits(); + U32 getTotalIssuedRequests(); + S32 getNumRequests(); + +private: + LLMutex mMutex; + S32 mConcurrency; + S32 mInQueue; //request currently in queue. + U32 mHandleCounter; + U32 mTotalIssuedRequests; + U32 mTotalReceivedBits; + + typedef struct _request_t + { + _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : + mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder) + {} + + U32 mHandle; + std::string mUrl; + LLCurlRequest::headers_t mHeaders; + S32 mOffset; + S32 mLength; + LLCurl::ResponderPtr mResponder; + U32 mPriority; + } request_t; + + struct request_compare + { + bool operator()(const request_t* lhs, const request_t* rhs) const + { + if(lhs->mPriority != rhs->mPriority) + { + return lhs->mPriority > rhs->mPriority; // higher priority in front of queue (set) + } + else + { + return (U32)lhs < (U32)rhs; + } + } + }; + + typedef std::set req_queue_t; + req_queue_t mCachedRequests; + std::map mRequestMap; +}; + class LLCurlEasyRequest { public: diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 8ac6acf877..1e1152ea85 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -151,7 +151,7 @@ public: /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) - + ~LLTextureFetchWorker(); // void relese() { --mActiveCount; } @@ -196,6 +196,8 @@ private: bool processSimulatorPackets(); bool writeToCacheComplete(); + void removeFromHTTPQueue(); + void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } @@ -275,6 +277,7 @@ private: S32 mRetryAttempt; S32 mActiveCount; U32 mGetStatus; + U32 mHTTPHandle; std::string mGetReason; // Work Data @@ -328,6 +331,7 @@ public: mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); } + S32 data_size = 0; lldebugs << "HTTP COMPLETE: " << mID << llendl; LLTextureFetchWorker* worker = mFetcher->getWorker(mID); if (worker) @@ -349,7 +353,7 @@ public: // llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; } - S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); + data_size = worker->callbackHttpGet(channels, buffer, partial, success); if(log_texture_traffic && data_size > 0) { @@ -359,9 +363,7 @@ public: gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; } } - - mFetcher->removeFromHTTPQueue(mID, data_size); - + if (worker->mMetricsStartTime) { LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, @@ -376,9 +378,10 @@ public: } else { - mFetcher->removeFromHTTPQueue(mID); - llwarns << "Worker not found: " << mID << llendl; + llwarns << "Worker not found: " << mID << llendl; } + + mFetcher->getCurlRequest().completeRequest(data_size); } virtual bool followRedir() @@ -692,7 +695,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), - mMetricsStartTime(0) + mMetricsStartTime(0), + mHTTPHandle(0) { mCanUseNET = mUrl.empty() ; @@ -726,7 +730,19 @@ LLTextureFetchWorker::~LLTextureFetchWorker() mFormattedImage = NULL; clearPackets(); unlockWorkMutex(); - mFetcher->removeFromHTTPQueue(mID); + + removeFromHTTPQueue(); +} + +void LLTextureFetchWorker::removeFromHTTPQueue() +{ + if(mHTTPHandle > 0) + { + llassert_always(mState == WAIT_HTTP_REQ); + + mFetcher->getCurlRequest().removeRequest(mHTTPHandle); + mHTTPHandle = 0; + } } void LLTextureFetchWorker::clearPackets() @@ -824,6 +840,7 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) mImagePriority = priority; calcWorkPriority(); U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); + mFetcher->getCurlRequest().updatePriority(mHTTPHandle, mWorkPriority); setPriority(work_priority); } } @@ -927,7 +944,7 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 size = mDesiredSize - offset; if (size <= 0) { - mState = CACHE_POST; + mState = CACHE_POST; //have enough data, will fall to decode return false; } mFileSize = 0; @@ -981,6 +998,9 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { + // + //This should never happen + // return false; } } @@ -1157,16 +1177,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { if(mCanUseHTTP) { - //NOTE: - //control the number of the http requests issued for: - //1, not openning too many file descriptors at the same time; - //2, control the traffic of http so udp gets bandwidth. - // - if(!mFetcher->canIssueHTTPRequest()) - { - return false ; //wait. - } - mFetcher->removeFromNetworkQueue(this, false); S32 cur_size = 0; @@ -1179,6 +1189,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; return false; } @@ -1209,7 +1220,6 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = WAIT_HTTP_REQ; - mFetcher->addToHTTPQueue(mID); if (! mMetricsStartTime) { mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); @@ -1227,8 +1237,9 @@ bool LLTextureFetchWorker::doWork(S32 param) { mRequestedSize = 0; } - res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, + mHTTPHandle = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, mWorkPriority, new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + res = true; } if (!res) { @@ -1292,6 +1303,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; return false; } @@ -1304,6 +1316,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = SEND_HTTP_REQ; return false; // retry } @@ -1366,6 +1379,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) { //timeout, abort. + removeFromHTTPQueue(); mState = DONE; return true; } @@ -1689,7 +1703,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, S32 data_size = 0 ; LLMutexLock lock(&mWorkMutex); - + mHTTPHandle = 0; if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1862,14 +1876,11 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureCache(cache), mImageDecodeThread(imagedecodethread), mTextureBandwidth(0), - mHTTPTextureBits(0), - mTotalHTTPRequests(0), mCurlGetRequest(NULL), mQAMode(qa_mode), mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), - mOriginFetchSource(LLTextureFetch::FROM_ALL), - mHTTPConcurrency(8) + mOriginFetchSource(LLTextureFetch::FROM_ALL) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); @@ -2035,21 +2046,6 @@ void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool c } } -// protected -void LLTextureFetch::addToHTTPQueue(const LLUUID& id) -{ - LLMutexLock lock(&mNetworkQueueMutex); - mHTTPTextureQueue.insert(id); - mTotalHTTPRequests++; -} - -void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) -{ - LLMutexLock lock(&mNetworkQueueMutex); - mHTTPTextureQueue.erase(id); - mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits -} - void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { lockQueue() ; @@ -2064,6 +2060,7 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; + worker->removeFromHTTPQueue(); worker->scheduleDelete(); } else @@ -2082,6 +2079,7 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; + worker->removeFromHTTPQueue(); worker->scheduleDelete(); } @@ -2094,37 +2092,6 @@ S32 LLTextureFetch::getNumRequests() return size ; } -bool LLTextureFetch::canIssueHTTPRequest() -{ - LLMutexLock lock(&mNetworkQueueMutex); - - return (S32)mHTTPTextureQueue.size() < mHTTPConcurrency ; -} - -S32 LLTextureFetch::getHTTPConcurrency() -{ - //LLMutexLock lock(&mNetworkQueueMutex); - return mHTTPConcurrency; -} - -S32 LLTextureFetch::getNumHTTPRequests() -{ - mNetworkQueueMutex.lock() ; - S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock() ; - - return size ; -} - -U32 LLTextureFetch::getTotalNumHTTPRequests() -{ - mNetworkQueueMutex.lock() ; - U32 size = mTotalHTTPRequests ; - mNetworkQueueMutex.unlock() ; - - return size ; -} - // call lockQueue() first! LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { @@ -2210,7 +2177,7 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { worker->lockWorkMutex(); worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); res = true; } return res; @@ -2292,15 +2259,7 @@ S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl band_width(gSavedSettings,"ThrottleBandwidthKBPS"); - { - mNetworkQueueMutex.lock() ; - mMaxBandwidth = band_width ; - - gTextureList.sTextureBits += mHTTPTextureBits ; - mHTTPTextureBits = 0 ; - - mNetworkQueueMutex.unlock() ; - } + mMaxBandwidth = band_width ; S32 res = LLWorkerThread::update(max_time_ms); @@ -2311,13 +2270,23 @@ S32 LLTextureFetch::update(F32 max_time_ms) // won't work so don't bother trying if (LLStartUp::getStartupState() > STATE_AGENT_SEND) { - sendRequestListToSimulators(); + sendRequestListToSimulators(); } } if (!mThreaded) { commonUpdate(); + + if(mCurlGetRequest) + { + mCurlGetRequest->nextRequests(); + } + } + + if(mCurlGetRequest) + { + gTextureList.sTextureBits += mCurlGetRequest->getTotalReceivedBits(); } if(mFetchDebugger) @@ -2352,7 +2321,7 @@ void LLTextureFetch::shutDownImageDecodeThread() void LLTextureFetch::startThread() { // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); + mCurlGetRequest = new LLCurlTextureRequest(8); if(mFetchDebugger) { @@ -2377,6 +2346,8 @@ void LLTextureFetch::threadedUpdate() { llassert_always(mCurlGetRequest); + mCurlGetRequest->nextRequests(); + // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -3189,6 +3160,7 @@ public: llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl; } mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success); + mDebugger->getCurlGetRequest()->completeRequest(0); } virtual bool followRedir() { @@ -3621,16 +3593,23 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mNbCurlCompleted = mFetchingHistory.size(); return 0; } + S32 size = mFetchingHistory.size(); - S32 max_concurrency = mFetcher->getHTTPConcurrency(); - if (mNbCurlRequests == max_concurrency) - return mNbCurlRequests; + if (mNbCurlRequests == size) //all issued + { + return 0; + } - S32 size = mFetchingHistory.size(); + S32 counter = 8; + mNbCurlRequests = 0; for (S32 i = 0 ; i < size ; i++) { + mNbCurlRequests++; + if (mFetchingHistory[i].mCurlState != FetchEntry::CURL_NOT_DONE) + { continue; + } std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[i].mID.asString().c_str(); S32 requestedSize = mFetchingHistory[i].mRequestedSize; // We request the whole file if the size was not set. @@ -3639,16 +3618,11 @@ S32 LLTextureFetchDebugger::fillCurlQueue() requestedSize = (requestedSize == 33554432 ? 0 : requestedSize); std::vector headers; headers.push_back("Accept: image/x-j2c"); - bool res = mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, new LLDebuggerHTTPResponder(this, i)); - if (res) - { - mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; - mNbCurlRequests++; - // Hack - if (mNbCurlRequests == max_concurrency) - break; - } - else + mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, 0x10000, new LLDebuggerHTTPResponder(this, i)); + + mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; + counter--; + if(counter < 1) { break; } @@ -3879,6 +3853,7 @@ bool LLTextureFetchDebugger::update(F32 max_time) break; case HTTP_FETCHING: mCurlGetRequest->process(); + mCurlGetRequest->nextRequests(); LLCurl::getCurlThread()->update(1); if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size()) { @@ -3980,7 +3955,6 @@ void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& ch const LLIOPipe::buffer_ptr_t& buffer, bool partial, bool success) { - mNbCurlRequests--; if (success) { mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; @@ -4016,6 +3990,7 @@ void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& ch { // Fetch will have to be redone mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE; + mNbCurlRequests--; } else //skip { diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 4d17cd0b7b..e04c9124a0 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -81,8 +81,6 @@ public: U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); void dump(); S32 getNumRequests() ; - S32 getNumHTTPRequests() ; - U32 getTotalNumHTTPRequests() ; // Public for access by callbacks S32 getPending(); @@ -101,7 +99,7 @@ public: LLViewerAssetStats * main_stats); void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + LLCurlTextureRequest & getCurlRequest() { return *mCurlGetRequest; } bool isQAMode() const { return mQAMode; } @@ -109,14 +107,10 @@ public: inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } - bool canIssueHTTPRequest(); - S32 getHTTPConcurrency(); - protected: void addToNetworkQueue(LLTextureFetchWorker* worker); void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); void addToHTTPQueue(const LLUUID& id); - void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); void removeRequest(LLTextureFetchWorker* worker, bool cancel); // Overrides from the LLThread tree @@ -175,8 +169,8 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; - + LLCurlTextureRequest* mCurlGetRequest; + // Map of all requests by UUID typedef std::map map_t; map_t mRequestMap; @@ -191,11 +185,6 @@ private: F32 mMaxBandwidth; LLTextureInfo mTextureInfo; - U32 mHTTPTextureBits; - - //debug use - U32 mTotalHTTPRequests ; - // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the @@ -212,9 +201,6 @@ private: // use the LLCurl module's request counter as it isn't thread compatible. // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. LLAtomic32 mCurlPOSTRequestCount; - - //control http concurrency for texture fetching - S32 mHTTPConcurrency; //which is adaptive to the network situation at an instant public: // A probabilistically-correct indicator that the current @@ -332,7 +318,7 @@ private: LLTextureFetch* mFetcher; LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; + LLCurlTextureRequest* mCurlGetRequest; S32 mNumFetchedTextures; S32 mNumCacheHits; @@ -372,8 +358,9 @@ public: void clearHistory(); void addHistoryEntry(LLTextureFetchWorker* worker); - void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;} - + void setCurlGetRequest(LLCurlTextureRequest* request) { mCurlGetRequest = request;} + LLCurlTextureRequest* getCurlGetRequest() { return mCurlGetRequest;} + void startWork(e_debug_state state); void setStopDebug() {mStopDebug = TRUE;} void tryToStopDebug(); //stop everything diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 425bf7ee87..a40928ce2a 100755 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -514,7 +514,7 @@ void LLGLTexMemBar::draw() S32 v_offset = 0;//(S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f); F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); - U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ; + U32 total_http_requests = LLAppViewer::getTextureFetch()->getCurlRequest().getTotalIssuedRequests() ; //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); @@ -552,7 +552,7 @@ void LLGLTexMemBar::draw() LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(), LLLFSThread::sLocal->getPending(), LLImageRaw::sRawImageCount, - LLAppViewer::getTextureFetch()->getNumHTTPRequests(), + LLAppViewer::getTextureFetch()->getCurlRequest().getNumRequests(), LLAppViewer::getImageDecodeThread()->getPending(), gTextureList.mCreateTextureList.size()); -- cgit v1.2.3 From cbb14e223c4db312191d0fe0b12362298fbf9fbd Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 25 May 2012 14:08:33 -0600 Subject: fix for SH-3137: delay to issue new http fetching requests if http connection failure happens --- indra/llmessage/llcurl.cpp | 76 ++++++++++++++++++++++++++++++---------- indra/llmessage/llcurl.h | 8 +++-- indra/newview/lltexturefetch.cpp | 41 +++++++++++++++++----- 3 files changed, 95 insertions(+), 30 deletions(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index c06cf2489d..4cfa48cbd9 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -922,7 +922,7 @@ void LLCurlThread::CurlRequest::finishRequest(bool completed) { if(mMulti->isDead()) { - mCurlThread->deleteMulti(mMulti) ; + mCurlThread->deleteMulti(mMulti) ; } else { @@ -968,8 +968,8 @@ void LLCurlThread::killMulti(LLCurl::Multi* multi) if(multi->isValid()) { - multi->markDead() ; -} + multi->markDead() ; + } else { deleteMulti(multi) ; @@ -1233,6 +1233,7 @@ LLCurlTextureRequest::LLCurlTextureRequest(S32 concurrency) : mTotalIssuedRequests(0), mTotalReceivedBits(0) { + mGlobalTimer.reset(); } LLCurlTextureRequest::~LLCurlTextureRequest() @@ -1251,12 +1252,12 @@ LLCurlTextureRequest::~LLCurlTextureRequest() U32 LLCurlTextureRequest::getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, - LLCurl::ResponderPtr responder) + LLCurl::ResponderPtr responder, F32 delay_time) { U32 ret_val = 0; bool success = false; - if(mInQueue < mConcurrency) + if(mInQueue < mConcurrency && delay_time < 0.f) { success = LLCurlRequest::getByteRange(url, headers, offset, length, responder); } @@ -1271,6 +1272,11 @@ U32 LLCurlTextureRequest::getByteRange(const std::string& url, else { request_t* request = new request_t(mHandleCounter, url, headers, offset, length, pri, responder); + if(delay_time > 0.f) + { + request->mStartTime = mGlobalTimer.getElapsedTimeF32() + delay_time; + } + mCachedRequests.insert(request); mRequestMap[mHandleCounter] = request; ret_val = mHandleCounter; @@ -1297,29 +1303,50 @@ void LLCurlTextureRequest::completeRequest(S32 received_bytes) void LLCurlTextureRequest::nextRequests() { - while(!mCachedRequests.empty() && mInQueue < mConcurrency) + if(mCachedRequests.empty() || mInQueue >= mConcurrency) { - request_t* request; - - { - LLMutexLock lock(&mMutex); - req_queue_t::iterator iter = mCachedRequests.begin(); - request = *iter; - } + return; + } - if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) - { - break; - } + F32 cur_time = mGlobalTimer.getElapsedTimeF32(); + req_queue_t::iterator iter; + { + LLMutexLock lock(&mMutex); + iter = mCachedRequests.begin(); + } + while(1) + { + request_t* request = *iter; + if(request->mStartTime < cur_time) { + if(!LLCurlRequest::getByteRange(request->mUrl, request->mHeaders, request->mOffset, request->mLength, request->mResponder)) + { + break; + } + LLMutexLock lock(&mMutex); + ++iter; mInQueue++; mTotalIssuedRequests++; - llassert_always(1 == mCachedRequests.erase(request)); + mCachedRequests.erase(request); mRequestMap.erase(request->mHandle); + delete request; + + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } + } + else + { + LLMutexLock lock(&mMutex); + ++iter; + if(iter == mCachedRequests.end() || mInQueue >= mConcurrency) + { + break; + } } - delete request; } return; @@ -1367,6 +1394,17 @@ void LLCurlTextureRequest::removeRequest(U32 handle) } } +bool LLCurlTextureRequest::isWaiting(U32 handle) +{ + if(!handle) + { + return false; + } + + LLMutexLock lock(&mMutex); + return mRequestMap.find(handle) != mRequestMap.end(); +} + U32 LLCurlTextureRequest::getTotalReceivedBits() { LLMutexLock lock(&mMutex); diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 04a2bd4287..87cb192141 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -420,7 +420,7 @@ public: LLCurlTextureRequest(S32 concurrency); ~LLCurlTextureRequest(); - U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder); + U32 getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder, F32 delay_time = -1.f); void nextRequests(); void completeRequest(S32 received_bytes); @@ -430,6 +430,7 @@ public: U32 getTotalReceivedBits(); U32 getTotalIssuedRequests(); S32 getNumRequests(); + bool isWaiting(U32 handle); private: LLMutex mMutex; @@ -442,7 +443,7 @@ private: typedef struct _request_t { _request_t(U32 handle, const std::string& url, const headers_t& headers, S32 offset, S32 length, U32 pri, LLCurl::ResponderPtr responder) : - mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder) + mHandle(handle), mUrl(url), mHeaders(headers), mOffset(offset), mLength(length), mPriority(pri), mResponder(responder), mStartTime(0.f) {} U32 mHandle; @@ -452,6 +453,7 @@ private: S32 mLength; LLCurl::ResponderPtr mResponder; U32 mPriority; + F32 mStartTime; //start time to issue this request } request_t; struct request_compare @@ -472,6 +474,8 @@ private: typedef std::set req_queue_t; req_queue_t mCachedRequests; std::map mRequestMap; + + LLFrameTimer mGlobalTimer; }; class LLCurlEasyRequest diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2a94564fe0..a778a1ccb0 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -278,6 +278,7 @@ private: S32 mActiveCount; U32 mGetStatus; U32 mHTTPHandle; + F32 mDelay; std::string mGetReason; // Work Data @@ -696,7 +697,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), mMetricsStartTime(0), - mHTTPHandle(0) + mHTTPHandle(0), + mDelay(-1.f) { mCanUseNET = mUrl.empty() ; @@ -929,6 +931,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mCacheWriteHandle = LLTextureCache::nullHandle(); mState = LOAD_FROM_TEXTURE_CACHE; mInCache = FALSE; + mDelay = -1.f; mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; @@ -1238,7 +1241,8 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedSize = 0; } mHTTPHandle = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, mWorkPriority, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true), mDelay); + mDelay = -1.f; //reset res = true; } if (!res) @@ -1286,12 +1290,15 @@ bool LLTextureFetchWorker::doWork(S32 param) ++mHTTPFailCount; max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; + mDelay = 2.0f; //delay 2 second to re-issue the http request } else { const S32 HTTP_MAX_RETRY_COUNT = 3; max_attempts = HTTP_MAX_RETRY_COUNT + 1; ++mHTTPFailCount; + mDelay = 2.0f; //delay 2 second to re-issue the http request + llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; @@ -1376,13 +1383,29 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) - { - //timeout, abort. - removeFromHTTPQueue(); - mState = DONE; - return true; - } + // + //No need to timeout, the responder should be triggered automatically. + // + + //if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) + //{ + // if(mFetcher->getCurlRequest().isWaiting(mHTTPHandle)) + // { + // mRequestedTimer.reset(); //still waiting, request not issued yet. + // } + // else if(!mHTTPWaitCount) + // { + // mHTTPWaitCount++; + // mRequestedTimer.reset(); //wait for one more FETCHING_TIMEOUT cycle in case the request is just issued. + // } + // else + // { + // //timeout, abort. + // removeFromHTTPQueue(); + // mState = DONE; + // return true; + // } + //} setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; -- cgit v1.2.3 From 3a63d88e7b48397cbfd508c05632453653e678e3 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 25 May 2012 14:59:13 -0600 Subject: trivial: fix a linux compiling error. --- indra/newview/lltexturefetch.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a778a1ccb0..e307de329b 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -870,8 +870,6 @@ void LLTextureFetchWorker::startWork(S32 param) // Called from LLWorkerThread::processRequest() bool LLTextureFetchWorker::doWork(S32 param) { - static const F32 FETCHING_TIMEOUT = 120.f;//seconds - LLMutexLock lock(&mWorkMutex); if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) -- cgit v1.2.3 From a9ef160f4eca2c6a55e2df286c9c75a27e38a831 Mon Sep 17 00:00:00 2001 From: Nyx Linden Date: Tue, 29 May 2012 23:01:19 +0000 Subject: SH-3135 Viewer UI corruption Presumed to be a complete failure of texture pipeline to decode anything. Got a fix from bao, a flag was not initialized properly in the texture pipeline. --- indra/newview/lltexturefetch.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index e307de329b..1e34954f79 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1905,7 +1905,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), - mOriginFetchSource(LLTextureFetch::FROM_ALL) + mOriginFetchSource(LLTextureFetch::FROM_ALL), + mFetcherLocked(FALSE) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); -- cgit v1.2.3 From c0a79227d71fbfdd2bfa6402f92d35d0c23d7f92 Mon Sep 17 00:00:00 2001 From: Tank_Master Date: Tue, 29 May 2012 16:52:57 -0700 Subject: Add individual volume controls to pannel_volume_pulldown Also includes translations for some of the other languages --- doc/contributions.txt | 3 +- .../skins/default/xui/de/panel_volume_pulldown.xml | 15 + .../skins/default/xui/en/panel_volume_pulldown.xml | 310 +++++++++++++++++++-- .../skins/default/xui/es/panel_volume_pulldown.xml | 14 + .../skins/default/xui/it/panel_volume_pulldown.xml | 15 + .../skins/default/xui/pl/panel_volume_pulldown.xml | 14 + 6 files changed, 346 insertions(+), 25 deletions(-) create mode 100644 indra/newview/skins/default/xui/de/panel_volume_pulldown.xml create mode 100644 indra/newview/skins/default/xui/es/panel_volume_pulldown.xml create mode 100644 indra/newview/skins/default/xui/it/panel_volume_pulldown.xml create mode 100644 indra/newview/skins/default/xui/pl/panel_volume_pulldown.xml diff --git a/doc/contributions.txt b/doc/contributions.txt index 04edfb4bea..df0aab4a89 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -1092,8 +1092,9 @@ Synystyr Texan Takeda Terrawyng TankMaster Finesmith STORM-1100 - STORM-1602 STORM-1258 + STORM-1602 + STORM-1868 VWR-26622 Talamasca Tali Rosca diff --git a/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml new file mode 100644 index 0000000000..e6ab9165d7 --- /dev/null +++ b/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + Date: Thu, 7 Jun 2012 14:02:19 -0700 Subject: remove gear icon --- .../newview/skins/default/xui/en/panel_volume_pulldown.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/indra/newview/skins/default/xui/en/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/en/panel_volume_pulldown.xml index 66a2ddeea8..6adede0362 100644 --- a/indra/newview/skins/default/xui/en/panel_volume_pulldown.xml +++ b/indra/newview/skins/default/xui/en/panel_volume_pulldown.xml @@ -12,20 +12,6 @@ layout="topleft" name="volumepulldown_floater" width="225"> - Date: Thu, 7 Jun 2012 17:46:22 -0400 Subject: Lock/Mutex fixes, documentation, new resource wait state, dtor cleanups Went through all the code and tried to document lock and thread usage in the module. There's a huge comment block introducing all of this at the beginning and I believe it's correct (though not quite complete). Keep it updated, people. Added a new state, WAIT_HTTP_RESOURCE, that's sort of a side-state of SEND_HTTP_REQ. If we hit a high-water mark for HTTP requests, the extra are shunted to the new state once. Once levels fall to a low-water mark, we run through a wait list of UUIDs, sort the valid ones by priority and release them for service. This keeps the HTTP layer busy while leaving the active queue shallow enough that requests can still be re-prioritzed cheaply. Priority model changed. The new state uses the PRIORITY_LOW mask, the old users of _LOW are now at PRIORITY_NORMAL and sleepers woken up after an external event are kicked off at PRIORITY_HIGH. This combination along with the new state should avoid priority inversion and keep things running without resorting to an infinite pipeline. New state displays as "HTW" with green text in the texture console. Request cancelation and worker run-down should now be more correct but this edge case may need more attention. --- indra/newview/lltexturefetch.cpp | 942 ++++++++++++++++++++++++++------------- indra/newview/lltexturefetch.h | 153 ++++++- indra/newview/lltextureview.cpp | 3 +- 3 files changed, 777 insertions(+), 321 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f9294b4cd1..23232cb590 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -52,6 +52,7 @@ #include "llviewerassetstats.h" #include "llworld.h" #include "llsdserialize.h" +#include "llviewertexturelist.h" // debug #include "httprequest.h" #include "httphandler.h" @@ -60,6 +61,102 @@ ////////////////////////////////////////////////////////////////////////////// +// +// Introduction +// +// This is an attempt to document what's going on in here after-the-fact. +// It's a sincere attempt to be accurate but there will be mistakes. +// +// Purpose +// +// (What is this solving?) +// +// Threads +// +// Several threads are actively invoking code in this module. They +// include: +// +// 1. Tmain Main thread of execution +// 2. Ttf LLTextureFetch's worker thread provided by LLQueuedThread +// 3. Tcurl LLCurl's worker thread (should disappear over time) +// 4. Ttc LLTextureCache's worker thread +// 5. Tid Image decoder's worker thread +// 6. Thl HTTP library's worker thread +// +// Mutexes/Condition Variables +// +// 1. Mt Mutex defined for LLThread's condition variable (base class of +// LLTextureFetch) +// 2. Ct Condition variable for LLThread and used by lock/unlockData(). +// 3. Mwtd Special LLWorkerThread mutex used for request deletion +// operations (base class of LLTextureFetch) +// 4. Mfq LLTextureFetch's mutex covering request and command queue +// data. +// 5. Mfnq LLTextureFetch's mutex covering udp and http request +// queue data. +// 6. Mwc Mutex covering LLWorkerClass's members (base class of +// LLTextureFetchWorker). One per request. +// 7. Mw LLTextureFetchWorker's mutex. One per request. +// +// Lock Ordering Rules +// +// Not an exhaustive list but shows the order of lock acquisition +// needed to prevent deadlocks. 'A < B' means acquire 'A' before +// acquiring 'B'. +// +// 1. Mw < Mfnq +// +// Method and Member Definitions +// +// With the above, we'll try to document what threads can call what +// methods (using T* for any), what locks must be held on entry and +// are taken out during execution and what data is covered by which +// lock (if any). This latter category will be especially prone to +// error so be skeptical. +// +// A line like: "// Locks: M" indicates a method that must +// be invoked by a caller holding the 'M' lock. Similarly, +// "// Threads: T" means that a caller should be running in +// the indicated thread. +// +// For data members, a trailing comment like "// M" means that +// the data member is covered by the specified lock. Absence of a +// comment can mean the member is unlocked or that I didn't bother +// to do the archaeology. In the case of LLTextureFetchWorker, +// most data members added by the leaf class are actually covered +// by the Mw lock. +// +// In code, a trailing comment like "// [-+]M" indicates a +// lock acquision or release point. +// +// +// Worker Lifecycle +// +// (Can't unilaterally delete, cleanup is two-phase, etc.) +// +// Worker State Machine +// +// (ASCII art needed) +// +// Priority Scheme +// +// [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state +// [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event +// [PRIORITY_HIGH, PRIORITY_URGENT) - rapidly transitioning through states, +// no waiting allowed +// +// + +////////////////////////////////////////////////////////////////////////////// + +// Tuning/Parameterization Constants + +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; + + +////////////////////////////////////////////////////////////////////////////// + class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler { @@ -69,11 +166,15 @@ private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: + + // Threads: Ttf CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) : mFetcher(fetcher), mID(id) { setImage(image); } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -90,10 +191,14 @@ private: class CacheWriteResponder : public LLTextureCache::WriteResponder { public: + + // Threads: Ttf CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) : mFetcher(fetcher), mID(id) { } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -110,10 +215,14 @@ private: class DecodeResponder : public LLImageDecodeThread::Responder { public: + + // Threads: Ttf DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } + + // Threads: Tid virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -146,20 +255,34 @@ private: }; public: + + // Threads: Ttf /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() ~LLTextureFetchWorker(); - // void relese() { --mActiveCount; } + // Threads: Ttf + // Locks: Mw S32 callbackHttpGet(LLCore::HttpResponse * response, bool partial, bool success); + + // Threads: Ttc void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); + + // Threads: Ttc void callbackCacheWrite(bool success); + + // Threads: Tid void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); + // Threads: T* void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -174,6 +297,7 @@ public: LLTextureFetch & getFetcher() { return *mFetcher; } // Inherited from LLCore::HttpHandler + // Threads: Ttf virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: @@ -181,27 +305,54 @@ protected: F32 priority, S32 discard, S32 size); private: + + // Threads: Tmain /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + + // Threads: Tmain /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Locks: Mw void resetFormattedData(); + // Locks: Mw void setImagePriority(F32 priority); + + // Locks: Mw (ctor invokes without lock) void setDesiredDiscard(S32 discard, S32 size); + + // Threads: T* + // Locks: Mw bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw void clearPackets(); + + // Locks: Mw void setupPacketData(); + + // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); + + // Locks: Mw void removeFromCache(); + + // Threads: Ttf + // Locks: Mw bool processSimulatorPackets(); + + // Threads: Ttf bool writeToCacheComplete(); - void lockWorkMutex() { mWorkMutex.lock(); } - void unlockWorkMutex() { mWorkMutex.unlock(); } - + // Threads: Ttf void recordTextureStart(bool is_http); + + // Threads: Ttf void recordTextureDone(bool is_http); + void lockWorkMutex() { mWorkMutex.lock(); } + void unlockWorkMutex() { mWorkMutex.unlock(); } + private: enum e_state // mState { @@ -215,8 +366,9 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, - WAIT_HTTP_REQ, + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -297,17 +449,15 @@ private: LLViewerAssetStats::duration_t mMetricsStartTime; - LLCore::HttpHandle mHttpHandle; - LLCore::BufferArray * mHttpBufferArray; + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data int mHttpPolicyClass; - bool mHttpActive; + bool mHttpActive; // Active request to http library unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; + bool mHttpReleased; // Has been released from resource wait once }; -////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////////// // Cross-thread messaging for asset metrics. @@ -542,12 +692,13 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", "SEND_HTTP_REQ", + "WAIT_HTTP_RESOURCE", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", "WAIT_ON_WRITE", - "DONE", + "DONE" }; // static @@ -608,7 +759,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpActive(false), mHttpReplySize(0U), - mHttpReplyOffset(0U) + mHttpReplyOffset(0U), + mHttpReleased(true) { mCanUseNET = mUrl.empty() ; @@ -630,12 +782,13 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); + + lockWorkMutex(); // +Mw (should be useless) if (mHttpActive) { - LL_WARNS("Texture") << "Deleting worker object while HTTP request is active." - << LL_ENDL; + // Issue a cancel on a live request... + mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); } - lockWorkMutex(); if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -646,15 +799,17 @@ LLTextureFetchWorker::~LLTextureFetchWorker() } mFormattedImage = NULL; clearPackets(); - unlockWorkMutex(); - mFetcher->removeFromHTTPQueue(mID); if (mHttpBufferArray) { mHttpBufferArray->release(); mHttpBufferArray = NULL; } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID); + mFetcher->removeHttpWaiter(mID); } +// Locks: Mw void LLTextureFetchWorker::clearPackets() { for_each(mPackets.begin(), mPackets.end(), DeletePointer()); @@ -664,6 +819,7 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +// Locks: Mw void LLTextureFetchWorker::setupPacketData() { S32 data_size = 0; @@ -696,6 +852,7 @@ void LLTextureFetchWorker::setupPacketData() } } +// Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); @@ -705,7 +862,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() return mWorkPriority; } -// mWorkMutex is locked +// Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -741,6 +898,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) } } +// Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { // llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); @@ -754,6 +912,7 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) } } +// Locks: Mw void LLTextureFetchWorker::resetFormattedData() { if (mHttpBufferArray) @@ -768,18 +927,19 @@ void LLTextureFetchWorker::resetFormattedData() mHaveAllData = FALSE; } -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { llassert(mFormattedImage.isNull()); } -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() +// Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - LLMutexLock lock(&mWorkMutex); + // Release waiters while we aren't holding the Mw lock. + mFetcher->releaseHttpWaiters(); + + LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) { @@ -863,7 +1023,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); @@ -873,7 +1033,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, @@ -1002,7 +1162,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); recordTextureStart(false); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } @@ -1013,7 +1173,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); //recordTextureStart(false); - //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + //setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } } @@ -1042,7 +1202,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mFetcher->addToNetworkQueue(this); // failsafe - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); recordTextureStart(false); } return false; @@ -1050,89 +1210,100 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == SEND_HTTP_REQ) { - if(mCanUseHTTP) - { - //NOTE: - //control the number of the http requests issued for: - //1, not openning too many file descriptors at the same time; - //2, control the traffic of http so udp gets bandwidth. - // - // static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8; - // *FIXME: For the moment, allow everything to transition into HTTP - // and have the new library order and throttle. - // if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) - // { - // return false ; //wait. - // } + if (! mCanUseHTTP) + { + return true; // abort + } - mFetcher->removeFromNetworkQueue(this, false); - - S32 cur_size = 0; - if (mFormattedImage.notNull()) + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + if (! mHttpReleased) + { + // If this request hasn't been released before and it looks like + // we're busy, put this request into resource wait and allow something + // else to come to the front. + if (mFetcher->getNumHTTPRequests() >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER || + mFetcher->getHttpWaitersCount()) { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) - { - if(cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - return true ; //abort. - } - } + mState = WAIT_HTTP_RESOURCE; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + return false; } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - mRequestedOffset = cur_size; + } + + mFetcher->removeFromNetworkQueue(this, false); - mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; - if (!mUrl.empty()) - { - mLoaded = FALSE; - mGetStatus = LLCore::HttpStatus(); - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - - // Will call callbackHttpGet when curl request completes - // *FIXME: enable redirection follow - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mWorkPriority, - mUrl, - mRequestedOffset, - mRequestedSize, - mFetcher->mHttpOptions, - mFetcher->mHttpHeaders, - this); - } - if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - ++mHTTPFailCount; - return true; // failed + if (cur_size > 0) + { + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + mState = DECODE_IMAGE; + return false; + } + else + { + return true; // abort. + } } - - mHttpActive = true; - mFetcher->addToHTTPQueue(mID); - recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - // fall through } - else //can not use http fetch. - { - return true ; //abort + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + ++mHTTPFailCount; + return true; // failed } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through + } + + if (mState == WAIT_HTTP_RESOURCE) + { + // Nothing to do until releaseHttpWaiters() puts us back + // into the flow... + return false; } if (mState == WAIT_HTTP_REQ) @@ -1148,7 +1319,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; - //roll back to try UDP + // roll back to try UDP if (mCanUseNET) { mState = INIT; @@ -1263,7 +1434,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return false; } } @@ -1272,7 +1443,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) { // for debug use, don't decode @@ -1380,7 +1551,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } llassert_always(datasize); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; @@ -1422,14 +1593,15 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); return true; } } return false; -} +} // -Mw +// Threads: Ttf // virtual void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { @@ -1437,6 +1609,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe static LLCachedControl log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); static LLCachedControl log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + LLMutexLock lock(&mWorkMutex); // +Mw + mHttpActive = false; if (log_to_viewer_log || log_to_sim) @@ -1494,10 +1668,10 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe mFetcher->removeFromHTTPQueue(mID); recordTextureDone(true); -} +} // -Mw -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { if (mDecodeHandle != 0) @@ -1510,6 +1684,8 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf + // virtual void LLTextureFetchWorker::finishWork(S32 param, bool completed) { @@ -1526,6 +1702,13 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) } } +// LLQueuedThread's update() method is asking if it's okay to +// delete this worker. You'll notice we're not locking in here +// which is a slight concern. Caller is expected to have made +// this request 'quiet' by whatever means... +// +// Threads: Tmain + // virtual bool LLTextureFetchWorker::deleteOK() { @@ -1572,6 +1755,7 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } +// Threads: Ttf void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1583,6 +1767,8 @@ void LLTextureFetchWorker::removeFromCache() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw bool LLTextureFetchWorker::processSimulatorPackets() { if (mFormattedImage.isNull() || mRequestedSize < 0) @@ -1643,13 +1829,13 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, bool partial, bool success) { S32 data_size = 0 ; - LLMutexLock lock(&mWorkMutex); - if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1732,10 +1918,11 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != LOAD_FROM_TEXTURE_CACHE) { // llwarns << "Read callback for " << mID << " with state = " << mState << llendl; @@ -1755,11 +1942,12 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw +// Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != WAIT_ON_WRITE) { // llwarns << "Write callback for " << mID << " with state = " << mState << llendl; @@ -1767,13 +1955,14 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) } mWritten = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Tid void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mDecodeHandle == 0) { return; // aborted, ignore @@ -1805,10 +1994,11 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag mDecoded = TRUE; // llinfos << mID << " : DECODE COMPLETE " << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { // Complete write to cache @@ -1831,6 +2021,7 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +// Threads: Ttf void LLTextureFetchWorker::recordTextureStart(bool is_http) { if (! mMetricsStartTime) @@ -1843,6 +2034,7 @@ void LLTextureFetchWorker::recordTextureStart(bool is_http) } +// Threads: Ttf void LLTextureFetchWorker::recordTextureDone(bool is_http) { if (mMetricsStartTime) @@ -1892,8 +2084,6 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image LLTextureFetch::~LLTextureFetch() { - cancelHttpRequests(); - clearDeleteList(); while (! mCommands.empty()) @@ -1915,6 +2105,8 @@ LLTextureFetch::~LLTextureFetch() mHttpHeaders = NULL; } + mHttpWaitResource.clear(); + delete mHttpRequest; mHttpRequest = NULL; @@ -1978,7 +2170,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con { return false; // need to wait for previous aborted request to complete } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setImagePriority(priority); @@ -1987,41 +2179,44 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } else { - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else { worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); - lockQueue() ; + lockQueue(); // +Mfq mRequestMap[id] = worker; - unlockQueue() ; + unlockQueue(); // -Mfq - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } // llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } + +// Threads: T* (but Ttf in practice) + // protected void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { - lockQueue() ; + lockQueue(); // +Mfq bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue() ; + unlockQueue(); // -Mfq - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq if (in_request_map) { // only add to the queue if in the request map @@ -2033,63 +2228,50 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { iter1->second.erase(worker->mID); } -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq size_t erased = mNetworkQueue.erase(worker->mID); if (cancel && erased > 0) { mCancelQueue[worker->mHost].insert(worker->mID); } -} +} // -Mfnq +// Threads: T* +// // protected void LLTextureFetch::addToHTTPQueue(const LLUUID& id) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.insert(id); mTotalHTTPRequests++; -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq mHTTPTextureQueue.erase(id); mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits -} - -void LLTextureFetch::cancelHttpRequests() -{ - for (queue_t::iterator iter(mHTTPTextureQueue.begin()); - mHTTPTextureQueue.end() != iter; - ++iter) - { - LLTextureFetchWorker* worker = getWorker(*iter); - if (worker && worker->mHttpActive) - { - mHttpRequest->requestCancel(worker->mHttpHandle, NULL); - } - } - - // *FIXME: Do this better with less time wasting. - int tries(10); - while (! mHTTPTextureQueue.empty() && --tries) - { - mHttpRequest->update(100); - ms_sleep(100); - } -} +} // -Mfnq +// NB: If you change deleteRequest() you should probably make +// parallel changes in removeRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); if (worker) { size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; @@ -2100,15 +2282,20 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) } else { - unlockQueue() ; + unlockQueue(); // -Mfq } } +// NB: If you change removeRequest() you should probably make +// parallel changes in deleteRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; removeFromNetworkQueue(worker, cancel); @@ -2117,34 +2304,39 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) worker->scheduleDelete(); } +// Threads: T* S32 LLTextureFetch::getNumRequests() { - lockQueue() ; + lockQueue(); // +Mfq S32 size = (S32)mRequestMap.size(); - unlockQueue() ; + unlockQueue(); // -Mfq - return size ; + return size; } +// Threads: T* S32 LLTextureFetch::getNumHTTPRequests() { - mNetworkQueueMutex.lock() ; + mNetworkQueueMutex.lock(); // +Mfq S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } +// Threads: T* U32 LLTextureFetch::getTotalNumHTTPRequests() { - mNetworkQueueMutex.lock() ; - U32 size = mTotalHTTPRequests ; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } // call lockQueue() first! +// Threads: T* +// Locks: Mfq LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LLTextureFetchWorker* res = NULL; @@ -2156,14 +2348,16 @@ LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) return res; } +// Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex) ; + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id) ; -} + return getWorkerAfterLock(id); +} // -Mfq +// Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux) { @@ -2186,17 +2380,17 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; res = true; LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } else { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -2206,7 +2400,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, raw = worker->mRawImage; aux = worker->mAuxImage; } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else @@ -2216,15 +2410,16 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, return res; } +// Threads: T* bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { bool res = false; LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw res = true; } return res; @@ -2239,23 +2434,24 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) // in step, at least until this can be refactored and // the redundancy eliminated. // -// May be called from any thread +// Threads: T* //virtual S32 LLTextureFetch::getPending() { S32 res; - lockData(); + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq res = mRequestQueue.size(); res += mCommands.size(); - } - unlockData(); + } // -Mfq + unlockData(); // -Ct return res; } +// Locks: Ct // virtual bool LLTextureFetch::runCondition() { @@ -2270,10 +2466,10 @@ bool LLTextureFetch::runCondition() bool have_no_commands(false); { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq have_no_commands = mCommands.empty(); - } + } // -Mfq return ! (have_no_commands && (mRequestQueue.empty() && mIdleThread)); // From base class @@ -2281,7 +2477,7 @@ bool LLTextureFetch::runCondition() ////////////////////////////////////////////////////////////////////////////// -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +// Threads: Ttf void LLTextureFetch::commonUpdate() { // Run a cross-thread command, if any. @@ -2306,20 +2502,21 @@ void LLTextureFetch::commonUpdate() } -// MAIN THREAD +// Threads: Tmain + //virtual S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl band_width(gSavedSettings,"ThrottleBandwidthKBPS"); { - mNetworkQueueMutex.lock() ; - mMaxBandwidth = band_width ; + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width; - gTextureList.sTextureBits += mHTTPTextureBits ; - mHTTPTextureBits = 0 ; + gTextureList.sTextureBits += mHTTPTextureBits; + mHTTPTextureBits = 0; - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq } S32 res = LLWorkerThread::update(max_time_ms); @@ -2337,7 +2534,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) return res; } -//called in the MAIN thread after the TextureCacheThread shuts down. +// called in the MAIN thread after the TextureCacheThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownTextureCacheThread() { if(mTextureCache) @@ -2347,7 +2546,9 @@ void LLTextureFetch::shutDownTextureCacheThread() } } -//called in the MAIN thread after the ImageDecodeThread shuts down. +// called in the MAIN thread after the ImageDecodeThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) @@ -2357,22 +2558,21 @@ void LLTextureFetch::shutDownImageDecodeThread() } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::startThread() { } -// WORKER THREAD +// Threads: Ttf // // This detaches the texture fetch thread from the LLCore // HTTP library but doesn't stop the thread running in that // library... void LLTextureFetch::endThread() { - cancelHttpRequests(); } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::threadedUpdate() { llassert_always(mHttpRequest); @@ -2406,6 +2606,7 @@ void LLTextureFetch::threadedUpdate() ////////////////////////////////////////////////////////////////////////////// +// Threads: Tmain void LLTextureFetch::sendRequestListToSimulators() { // All requests @@ -2431,48 +2632,48 @@ void LLTextureFetch::sendRequestListToSimulators() typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; { - LLMutexLock lock2(&mNetworkQueueMutex); - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) - { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { - // We already received our URL, remove from the queue - llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) - { - // We have all the packets... make sure this is high priority + queue_t::iterator curiter = iter++; + LLTextureFetchWorker* req = getWorker(*curiter); + if (!req) + { + mNetworkQueue.erase(curiter); + continue; // paranoia + } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } + if (req->mID == mDebugID) + { + mDebugCount++; // for setting breakpoints + } + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) + { + // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + continue; + } + F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - } - } + } // -Mfnq for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) @@ -2495,9 +2696,9 @@ void LLTextureFetch::sendRequestListToSimulators() if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // Initialize packet data based on data read from cache - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->setupPacketData(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw } if (0 == sim_request_count) { @@ -2526,12 +2727,12 @@ void LLTextureFetch::sendRequestListToSimulators() mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); } - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw sim_request_count++; if (sim_request_count >= IMAGES_PER_REQUEST) { @@ -2552,55 +2753,57 @@ void LLTextureFetch::sendRequestListToSimulators() // Send cancelations { - LLMutexLock lock2(&mNetworkQueueMutex); - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + if (gMessageSystem && !mCancelQueue.empty()) { - LLHost host = iter1->first; - if (host == LLHost::invalid) - { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); + iter1 != mCancelQueue.end(); ++iter1) { - if (0 == request_count) + LLHost host = iter1->first; + if (host == LLHost::invalid) { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + host = gAgent.getRegionHost(); } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); + S32 request_count = 0; + for (queue_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) + { + if (0 == request_count) + { + gMessageSystem->newMessageFast(_PREHASH_RequestImage); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + gMessageSystem->nextBlockFast(_PREHASH_RequestImage); + gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); + gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); + gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); + gMessageSystem->addU32Fast(_PREHASH_Packet, 0); + gMessageSystem->addU8Fast(_PREHASH_Type, 0); // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + request_count++; + if (request_count >= IMAGES_PER_REQUEST) + { + gMessageSystem->sendSemiReliable(host, NULL, NULL); + request_count = 0; + } + } + if (request_count > 0 && request_count < IMAGES_PER_REQUEST) { gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } + mCancelQueue.clear(); } - mCancelQueue.clear(); - } - } + } // -Mfnq } ////////////////////////////////////////////////////////////////////////////// +// Threads: T* +// Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { mRequestedTimer.reset(); @@ -2633,6 +2836,7 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +// Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) { @@ -2667,14 +2871,14 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw // Copy header data into image object worker->mImageCodec = codec; @@ -2685,10 +2889,12 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } + +// Threads: T* bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) { LLTextureFetchWorker* worker = getWorker(id); @@ -2713,14 +2919,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw res = worker->insertPacket(packet_num, data, data_size); @@ -2737,7 +2943,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 removeFromNetworkQueue(worker, true); // failsafe } - if(packet_num >= (worker->mTotalPackets - 1)) + if (packet_num >= (worker->mTotalPackets - 1)) { static LLCachedControl log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); @@ -2749,12 +2955,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); } } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } ////////////////////////////////////////////////////////////////////////////// + +// Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { BOOL from_cache = FALSE ; @@ -2762,14 +2970,15 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex() ; - from_cache = worker->mInLocalCache ; - worker->unlockWorkMutex() ; + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw } return from_cache ; } +// Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { @@ -2783,7 +2992,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -2810,7 +3019,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r } fetch_priority = worker->getPriority(); can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -2842,12 +3051,136 @@ void LLTextureFetch::dump() { llinfos << " ID: " << (*iter) << llendl; } + + llinfos << "LLTextureFetch WAIT_HTTP_RESOURCE:" << llendl; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } } +////////////////////////////////////////////////////////////////////////////// + +// HTTP Resource Waiting Methods + +// Threads: Ttf +void LLTextureFetch::addHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +// Locks: -Mw (must not hold any worker when called) +void LLTextureFetch::releaseHttpWaiters() +{ + if (HTTP_REQUESTS_IN_QUEUE_LOW_WATER < getNumHTTPRequests()) + return; + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + + typedef std::vector uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + + const size_t limit(mHttpWaitResource.size()); + tids.resize(limit); + wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); + for (int i(0); + i < limit && mHttpWaitResource.end() != iter; + ++i, ++iter) + { + tids[i] = *iter; + } + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. + typedef std::set worker_set_t; + worker_set_t tids2; + + for (uuid_vec_t::const_iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.insert(worker); + } + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + tids.clear(); + for (worker_set_t::iterator iter2(tids2.begin()); + tids2.end() != iter2 && 0 < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - getNumHTTPRequests()); + ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE != worker->mState) + { + worker->unlockWorkMutex(); // -Mw + continue; + } + worker->mHttpReleased = true; + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } +} + +// Threads: T* +void LLTextureFetch::cancelHttpWaiters() +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +int LLTextureFetch::getHttpWaitersCount() +{ + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + + ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods +// Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { TFReqSetRegion * req = new TFReqSetRegion(region_handle); @@ -2855,6 +3188,7 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, @@ -2865,6 +3199,7 @@ void LLTextureFetch::commandSendMetrics(const std::string & caps_url, cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandDataBreak() { // The pedantically correct way to implement this is to create a command @@ -2875,30 +3210,33 @@ void LLTextureFetch::commandDataBreak() LLTextureFetch::svMetricsDataBreak = true; } +// Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { - lockQueue(); + lockQueue(); // +Mfq mCommands.push_back(req); - unlockQueue(); + unlockQueue(); // -Mfq unpause(); } +// Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { TFRequest * ret = 0; - lockQueue(); + lockQueue(); // +Mfq if (! mCommands.empty()) { ret = mCommands.front(); mCommands.erase(mCommands.begin()); } - unlockQueue(); + unlockQueue(); // -Mfq return ret; } +// Threads: Ttf void LLTextureFetch::cmdDoWork() { if (mDebugPause) @@ -2932,6 +3270,8 @@ namespace class AssetReportHandler : public LLCore::HttpHandler { public: + + // Threads: Ttf virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { LLCore::HttpStatus status(response->getStatus()); @@ -3100,5 +3440,3 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics) } // end of anonymous namespace - - diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index cfea3aad9d..53b0f7885f 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -45,6 +45,7 @@ class LLHost; class LLViewerAssetStats; // Interface class + class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; @@ -55,67 +56,174 @@ public: class TFRequest; - /*virtual*/ S32 update(F32 max_time_ms); - void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. - void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + /*virtual*/ S32 update(F32 max_time_ms); + + // called in the main thread after the TextureCacheThread shuts down. + // Threads: Tmain + void shutDownTextureCacheThread(); + + //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + void shutDownImageDecodeThread(); + // Threads: T* (but Tmain mostly) bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + + // Requests that a fetch operation be deleted from the queue. + // If @cancel is true, also stops any I/O operations pending. + // Actual delete will be scheduled and performed later. + // + // Note: This *looks* like an override/variant of the + // base class's deleteRequest() but is functionally quite + // different. + // + // Threads: T* void deleteRequest(const LLUUID& id, bool cancel); + + // Threads: T* bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer& raw, LLPointer& aux); + + // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); + // Threads: T* bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); + + // Threads: T* bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + + // Threads: T* (but not safe) F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug + // Threads: T* BOOL isFromLocalCache(const LLUUID& id); + + // @return Magic number giving the internal state of the + // request. We should make these codes public if we're + // going to return them as a status value. + // + // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + + // Debug utility - generally not safe void dump(); - S32 getNumRequests() ; - S32 getNumHTTPRequests() ; - U32 getTotalNumHTTPRequests() ; + + // Threads: T* + S32 getNumRequests(); + + // Threads: T* + S32 getNumHTTPRequests(); + + // Threads: T* + U32 getTotalNumHTTPRequests(); - // Public for access by callbacks + // Threads: T* S32 getPending(); + + // Threads: T* void lockQueue() { mQueueMutex.lock(); } + + // Threads: T* void unlockQueue() { mQueueMutex.unlock(); } + + // Threads: T* LLTextureFetchWorker* getWorker(const LLUUID& id); + + // Threads: T* + // Locks: Mfq LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); // Commands available to other threads to control metrics gathering operations. + + // Threads: T* void commandSetRegion(U64 region_handle); + + // Threads: T* void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLViewerAssetStats * main_stats); + + // Threads: T* void commandDataBreak(); + // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } bool isQAMode() const { return mQAMode; } + // ---------------------------------- + // HTTP resource waiting methods + + // Threads: T* + void addHttpWaiter(const LLUUID & tid); + + // Threads: T* + void removeHttpWaiter(const LLUUID & tid); + + // If there are slots, release one or more LLTextureFetchWorker + // requests from resource wait state (WAIT_HTTP_RESOURCE) to + // active (SEND_HTTP_REQ). + // + // Because this will modify state of many workers, you may not + // hold any Mw lock while calling. This makes it a little + // inconvenient to use but that's the rule. + // + // Threads: T* + // Locks: -Mw (must not hold any worker when called) + void releaseHttpWaiters(); + + // Threads: T* + void cancelHttpWaiters(); + + // Threads: T* + int getHttpWaitersCount(); + // ---------------------------------- + protected: + // Threads: T* (but Ttf in practice) void addToNetworkQueue(LLTextureFetchWorker* worker); + + // Threads: T* void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + + // Threads: T* void addToHTTPQueue(const LLUUID& id); + + // Threads: T* void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0); + + // Identical to @deleteRequest but with different arguments + // (caller already has the worker pointer). + // + // Threads: T* void removeRequest(LLTextureFetchWorker* worker, bool cancel); - void cancelHttpRequests(); // Overrides from the LLThread tree + // Locks: Ct bool runCondition(); private: + // Threads: Tmain void sendRequestListToSimulators(); + + // Threads: Ttf /*virtual*/ void startThread(void); + + // Threads: Ttf /*virtual*/ void endThread(void); + + // Threads: Ttf /*virtual*/ void threadedUpdate(void); + + // Threads: Ttf void commonUpdate(); // Metrics command helpers @@ -126,6 +234,8 @@ private: * Takes ownership of the TFRequest object. * * Method locks the command queue. + * + * Threads: T* */ void cmdEnqueue(TFRequest *); @@ -136,6 +246,8 @@ private: * Caller acquires ownership of the object and must dispose of it. * * Method locks the command queue. + * + * Threads: T* */ TFRequest * cmdDequeue(); @@ -145,6 +257,8 @@ private: * additional commands. * * Method locks the command queue. + * + * Threads: Ttf */ void cmdDoWork(); @@ -164,29 +278,29 @@ private: // Map of all requests by UUID typedef std::map map_t; - map_t mRequestMap; + map_t mRequestMap; // Mfq // Set of requests that require network data typedef std::set queue_t; - queue_t mNetworkQueue; - queue_t mHTTPTextureQueue; + queue_t mNetworkQueue; // Mfnq + queue_t mHTTPTextureQueue; // Mfnq typedef std::map > cancel_queue_t; - cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; - F32 mMaxBandwidth; + cancel_queue_t mCancelQueue; // Mfnq + F32 mTextureBandwidth; // + F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; - U32 mHTTPTextureBits; + U32 mHTTPTextureBits; // Mfnq //debug use - U32 mTotalHTTPRequests ; + U32 mTotalHTTPRequests; // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the // same locks. typedef std::vector command_queue_t; - command_queue_t mCommands; + command_queue_t mCommands; // Mfq // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; @@ -197,6 +311,9 @@ private: LLCore::HttpRequest * mHttpRequest; LLCore::HttpOptions * mHttpOptions; LLCore::HttpHeaders * mHttpHeaders; + + typedef std::set wait_http_res_queue_t; + wait_http_res_queue_t mHttpWaitResource; // Mfnq public: // A probabilistically-correct indicator that the current diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 1c89766b26..5f1d7829ed 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -235,13 +235,14 @@ void LLTextureBar::draw() { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR { "REQ", LLColor4::yellow },// SEND_HTTP_REQ + { "HTW", LLColor4::green }, // WAIT_HTTP_RES { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 12 +#define LAST_STATE 13 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 -- cgit v1.2.3 From cab68bb04ed3c3734b48c703a43d41f231911647 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 7 Jun 2012 18:10:32 -0400 Subject: First efforts at getting the texture cache working. This is now avoiding doing HTTP fetches for read data. Not certain it's completely correct but the difference is already significant. --- indra/newview/lltexturefetch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 23232cb590..92b847345d 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1584,7 +1584,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DONE) { - if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) + if (mDecodedDiscard > 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT mState = INIT; @@ -1883,7 +1883,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had } - else if (data_size < mRequestedSize && mRequestedDiscard == 0) + else if (data_size < mRequestedSize /*&& mRequestedDiscard == 0*/) { // *FIXME: I think we can treat this as complete regardless // of requested discard level. Revisit this... -- cgit v1.2.3 From ebe5aff98b294aae6850889b3f9f482297417fa0 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 8 Jun 2012 10:49:43 -0400 Subject: avatar metrics - removed unused fields --- indra/newview/llvoavatarself.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index fbc84c1d32..0bfd0df769 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2132,9 +2132,7 @@ LLSD LLVOAvatarSelf::metricsData() { // runway - add region info LLSD result; - result["id"] = getID(); result["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus()); - result["is_self"] = isSelf(); std::vector rez_counts; LLVOAvatar::getNearbyRezzedStats(rez_counts); result["nearby"] = LLSD::emptyMap(); @@ -2148,7 +2146,6 @@ LLSD LLVOAvatarSelf::metricsData() result["timers"]["ruth"] = mRuthTimer.getElapsedTimeF32(); result["timers"]["invisible"] = mInvisibleTimer.getElapsedTimeF32(); result["timers"]["fully_loaded"] = mFullyLoadedTimer.getElapsedTimeF32(); - result["phases"] = getPhases().dumpPhases(); result["startup"] = LLStartUp::getPhases().dumpPhases(); return result; -- cgit v1.2.3 From 841a447e55a48dbf48d9508a0b5a42b7c77693b7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 8 Jun 2012 16:12:52 +0000 Subject: Limit libcurl's DNS resolution to IPV4 addresses for now. Callers who want to try IPV6 can still override at will using CURLOPT_IPRESOLVE. --- indra/llmessage/llcurl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 3bcaffc275..c444f82b51 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -308,6 +308,8 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); + result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result); ++gCurlEasyCount; return easy; @@ -494,7 +496,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); - + setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + // Set the CURL options for either Socks or HTTP proxy LLProxy::getInstance()->applyProxySettings(this); -- cgit v1.2.3 From a64bb32b0550afdf9cd54c4f3f38225a1fcc0b90 Mon Sep 17 00:00:00 2001 From: Jonathan Yap Date: Fri, 8 Jun 2012 18:05:31 -0400 Subject: STORM-1877 Missing entries in strings.xml for some Inventory folder names --- doc/contributions.txt | 1 + indra/newview/skins/default/xui/en/strings.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/contributions.txt b/doc/contributions.txt index 04edfb4bea..ee6bb279c0 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -629,6 +629,7 @@ Jonathan Yap STORM-1809 STORM-1793 STORM-1810 + STORM-1877 Kadah Coba STORM-1060 Jondan Lundquist diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 4ccec4838a..f565928d44 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2139,11 +2139,15 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale My Favorites + My Favorites + My Favorites Current Outfit Initial Outfits My Outfits Accessories Meshes + Received Items + Merchant Outbox Friends -- cgit v1.2.3 From 28a04400b4160dd34166483ddcf0c12637bcc363 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 8 Jun 2012 20:21:54 -0400 Subject: Implemented HTTP retry for requests. Went in rather easily which surprised me. Added a retry queue similar to ready queue to the policy object which is sorted by retry time. Currently do five retries (after the initial try) delayed by .25, .5, 1, 2 and 5 seconds. Removed the retry logic from the lltexturefetch module. Upped the waiting time in the unit test for the retries. People won't like this but tough, need tests. --- indra/llcorehttp/_httplibcurl.cpp | 58 +++++++++-- indra/llcorehttp/_httplibcurl.h | 4 +- indra/llcorehttp/_httpoprequest.cpp | 51 ++++++++-- indra/llcorehttp/_httpoprequest.h | 5 + indra/llcorehttp/_httppolicy.cpp | 98 ++++++++++++++----- indra/llcorehttp/_httppolicy.h | 19 +++- indra/llcorehttp/_httpreadyqueue.h | 2 - indra/llcorehttp/_httpretryqueue.h | 94 ++++++++++++++++++ indra/llcorehttp/httpcommon.h | 12 +++ indra/llcorehttp/tests/test_httprequest.hpp | 2 +- indra/newview/lltexturefetch.cpp | 145 ++++++++++++++++++++-------- indra/newview/lltexturefetch.h | 6 +- 12 files changed, 402 insertions(+), 94 deletions(-) create mode 100644 indra/llcorehttp/_httpretryqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 5272c391e8..05b2c2be69 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -29,6 +29,7 @@ #include "httpheaders.h" #include "bufferarray.h" #include "_httpoprequest.h" +#include "_httppolicy.h" namespace LLCore @@ -85,6 +86,8 @@ void HttpLibcurl::term() HttpService::ELoopSpeed HttpLibcurl::processTransport() { + HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); + // Give libcurl some cycles to do I/O & callbacks for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) { @@ -110,7 +113,8 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() CURL * handle(msg->easy_handle); CURLcode result(msg->data.result); - completeRequest(mMultiHandles[policy_class], handle, result); + HttpService::ELoopSpeed speed(completeRequest(mMultiHandles[policy_class], handle, result)); + ret = (std::min)(ret, speed); handle = NULL; // No longer valid on return } else if (CURLMSG_NONE == msg->msg) @@ -127,7 +131,11 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } } - return mActiveOps.empty() ? HttpService::REQUEST_SLEEP : HttpService::NORMAL; + if (! mActiveOps.empty()) + { + ret = (std::min)(ret, HttpService::NORMAL); + } + return ret; } @@ -153,8 +161,12 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } -void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); // *FIXME: check the pointer @@ -190,10 +202,7 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode int http_status(200); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); - op->mStatus = LLCore::HttpStatus(http_status, - (http_status >= 200 && http_status <= 299 - ? HE_SUCCESS - : HE_REPLY_ERROR)); + op->mStatus = LLCore::HttpStatus(http_status); } // Detach from multi and recycle handle @@ -201,9 +210,42 @@ void HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode curl_easy_cleanup(handle); op->mCurlHandle = NULL; - // Deliver to reply queue and release + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + HttpPolicy & policy(mService->getPolicy()); + + policy.retryOp(op); + return HttpService::NORMAL; // Having pushed to retry, keep things running + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; + } + op->stageFromActive(mService); op->release(); + return HttpService::REQUEST_SLEEP; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index ec325c1946..fe628b9ab0 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -83,7 +83,9 @@ public: protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. - void completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + HttpService::ELoopSpeed completeRequest(CURLM * multi_handle, + CURL * handle, + CURLcode status); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 4bdc4a5257..895629c514 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -99,8 +99,15 @@ HttpOpRequest::HttpOpRequest() mReplyBody(NULL), mReplyOffset(0), mReplyLength(0), - mReplyHeaders(NULL) -{} + mReplyHeaders(NULL), + mPolicyRetries(0), + mPolicyRetryAt(HttpTime(0)), + mPolicyRetryLimit(5) // *FIXME: Get from policy definitions +{ + // *NOTE: As members are added, retry initialization/cleanup + // may need to be extended in @prepareRequest(). +} + HttpOpRequest::~HttpOpRequest() @@ -130,7 +137,6 @@ HttpOpRequest::~HttpOpRequest() } mCurlService = NULL; - if (mCurlHeaders) { @@ -313,6 +319,30 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { + // Scrub transport and result data for retried op case + mCurlActive = false; + mCurlHandle = NULL; + mCurlService = NULL; + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + mCurlBodyPos = 0; + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + mReplyOffset = 0; + mReplyLength = 0; + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } + // *FIXME: better error handling later HttpStatus status; @@ -321,6 +351,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHandle = curl_easy_init(); // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); @@ -403,12 +434,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; } - if (mReqHeaders) - { - mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); - } - mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); - + // There's a CURLOPT for this now... if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) { static const char * const fmt1("Range: bytes=%lu-%lu"); @@ -428,6 +454,13 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + if (mReqHeaders) + { + // Caller's headers last to override + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 0cad4e8459..6dcf30ca0c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -128,6 +128,11 @@ public: off_t mReplyOffset; size_t mReplyLength; HttpHeaders * mReplyHeaders; + + // Policy data + int mPolicyRetries; + HttpTime mPolicyRetryAt; + const int mPolicyRetryLimit; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 51f5e487dc..1f4cd34a4b 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -24,39 +24,46 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "_httppolicy.h" #include "_httpoprequest.h" #include "_httpservice.h" #include "_httplibcurl.h" +#include "lltimer.h" + namespace LLCore { HttpPolicy::HttpPolicy(HttpService * service) : mService(service) -{ - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) - { - mReadyInClass[policy_class] = 0; - } -} +{} HttpPolicy::~HttpPolicy() { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue & readyq(mReadyQueue[policy_class]); + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + while (! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + op->cancel(); + op->release(); + retryq.pop(); + } + + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); while (! readyq.empty()) { HttpOpRequest * op(readyq.top()); op->cancel(); op->release(); - mReadyInClass[policy_class]--; readyq.pop(); } } @@ -68,27 +75,69 @@ void HttpPolicy::addOp(HttpOpRequest * op) { const int policy_class(op->mReqPolicy); - mReadyQueue[policy_class].push(op); - ++mReadyInClass[policy_class]; + op->mPolicyRetries = 0; + mState[policy_class].mReadyQueue.push(op); +} + + +void HttpPolicy::retryOp(HttpOpRequest * op) +{ + static const HttpTime retry_deltas[] = + { + 250000, // 1st retry in 0.25 S, etc... + 500000, + 1000000, + 2000000, + 5000000 // ... to every 5.0 S. + }; + static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1); + + const HttpTime now(totalTime()); + const int policy_class(op->mReqPolicy); + + const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); + op->mPolicyRetryAt = now + delta; + ++op->mPolicyRetries; + LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries + << " being scheduled for " << delta << " uSecs from now." + << LL_ENDL; + mState[policy_class].mRetryQueue.push(op); } HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { + const HttpTime now(totalTime()); HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); HttpLibcurl & transport(mService->getTransport()); - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue & readyq(mReadyQueue[policy_class]); int active(transport.getActiveCountInClass(policy_class)); int needed(8 - active); - if (needed > 0 && mReadyInClass[policy_class] > 0) + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + + if (needed > 0) { - // Scan ready queue for requests that match policy - - while (! readyq.empty() && needed > 0 && mReadyInClass[policy_class] > 0) + // First see if we have any retries... + while (needed > 0 && ! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + if (op->mPolicyRetryAt > now) + break; + + retryq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + + // Now go on to the new requests... + while (needed > 0 && ! readyq.empty()) { HttpOpRequest * op(readyq.top()); readyq.pop(); @@ -96,17 +145,16 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() op->stageFromReady(mService); op->release(); - --mReadyInClass[policy_class]; --needed; } } - - if (! readyq.empty()) + + if (! readyq.empty() || ! retryq.empty()) { // If anything is ready, continue looping... result = (std::min)(result, HttpService::NORMAL); } - } + } // end foreach policy_class return result; } @@ -114,9 +162,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { - HttpReadyQueue::container_type & c(mReadyQueue[policy_class].get_container()); + HttpReadyQueue::container_type & c(mState[policy_class].mReadyQueue.get_container()); // Scan ready queue for requests that match policy for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) @@ -126,9 +174,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior if (static_cast(*cur) == handle) { HttpOpRequest * op(*cur); - c.erase(cur); // All iterators are now invalidated + c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; - mReadyQueue[policy_class].push(op); // Re-insert using adapter class + mState[policy_class].mReadyQueue.push(op); // Re-insert using adapter class return true; } } diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 425079ec63..6f18264f3d 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -31,6 +31,7 @@ #include "httprequest.h" #include "_httpservice.h" #include "_httpreadyqueue.h" +#include "_httpretryqueue.h" #include "_httppolicyglobal.h" @@ -67,6 +68,14 @@ public: /// additional references will be added.) void addOp(HttpOpRequest *); + /// Similar to addOp, used when a caller wants to retry a + /// request that has failed. It's placed on a special retry + /// queue but ordered by retry time not priority. Otherwise, + /// handling is the same and retried operations are considered + /// before new ones but that doesn't guarantee completion + /// order. + void retryOp(HttpOpRequest *); + // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); @@ -77,10 +86,14 @@ public: return mGlobalOptions; } - protected: - int mReadyInClass[HttpRequest::POLICY_CLASS_LIMIT]; - HttpReadyQueue mReadyQueue[HttpRequest::POLICY_CLASS_LIMIT]; + struct State + { + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + }; + + State mState[HttpRequest::POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner HttpPolicyGlobal mGlobalOptions; diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 2cd96aefe3..87828834dc 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -36,8 +36,6 @@ namespace LLCore { -class HttpOpRequest; - /// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects. /// /// This uses the priority_queue adaptor class to provide the queue diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h new file mode 100644 index 0000000000..745adec09d --- /dev/null +++ b/indra/llcorehttp/_httpretryqueue.h @@ -0,0 +1,94 @@ +/** + * @file _httpretryqueue.h + * @brief Internal declaration for the operation retry queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RETRY_QUEUE_H_ +#define _LLCORE_HTTP_RETRY_QUEUE_H_ + + +#include + +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +struct HttpOpRetryCompare +{ + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt; + } +}; + + +typedef std::priority_queue, + LLCore::HttpOpRetryCompare> HttpRetryQueueBase; + +class HttpRetryQueue : public HttpRetryQueueBase +{ +public: + HttpRetryQueue() + : HttpRetryQueueBase() + {} + + ~HttpRetryQueue() + {} + +protected: + HttpRetryQueue(const HttpRetryQueue &); // Not defined + void operator=(const HttpRetryQueue &); // Not defined + +public: + const container_type & get_container() const + { + return c; + } + + container_type & get_container() + { + return c; + } + +}; // end class HttpRetryQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_RETRY_QUEUE_H_ diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index fd2661b700..42b75edb41 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -114,6 +114,9 @@ namespace LLCore typedef void * HttpHandle; #define LLCORE_HTTP_HANDLE_INVALID (NULL) +/// For internal scheduling and metrics, we use a microsecond +/// timebase compatible with the environment. +typedef U64 HttpTime; /// Error codes defined by the library itself as distinct from /// libcurl (or any other transport provider). @@ -180,6 +183,15 @@ struct HttpStatus mStatus(status) {} + HttpStatus(int http_status) + : mType(http_status), + mStatus(http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR) + { + llassert(http_status >= 100 && http_status <= 999); + } + HttpStatus(const HttpStatus & rhs) : mType(rhs.mType), mStatus(rhs.mStatus) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 0e9d7d8979..2d91b95347 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -381,7 +381,7 @@ void HttpRequestTestObjectType::test<5>() // Run the notification pump. int count(0); - int limit(20); + int limit(180); // With retries, can take more than 10 seconds to give up while (count++ < limit && mHandlerCalls < 1) { req->update(1000); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 92b847345d..4a46ea0e97 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -67,9 +67,53 @@ // This is an attempt to document what's going on in here after-the-fact. // It's a sincere attempt to be accurate but there will be mistakes. // +// // Purpose // -// (What is this solving?) +// What is this module trying to do? It accepts requests to load textures +// at a given priority and discard level and notifies the caller when done +// (successfully or not). Additional constraints are: +// +// * Support a local texture cache. Don't hit network when possible +// to avoid it. +// * Use UDP or HTTP as directed or as fallback. HTTP is tried when +// not disabled and a URL is available. UDP when a URL isn't +// available or HTTP attempts fail. +// * Asynchronous (using threads). Main thread is not to be blocked or +// burdened. +// * High concurrency. Many requests need to be in-flight and at various +// stages of completion. +// * Tolerate frequent re-prioritizations of requests. Priority is +// a reflection of a camera's viewpoint and as that viewpoint changes, +// objects and textures become more and less relevant and that is +// expressed at this level by priority changes and request cancelations. +// +// The caller interfaces that fall out of the above and shape the +// implementation are: +// * createRequest - Load j2c image via UDP or HTTP at given discard level and priority +// * deleteRequest - Request removal of prior request +// * getRequestFinished - Test if request is finished returning data to caller +// * updateRequestPriority - Change priority of existing request +// * getFetchState - Retrieve progress on existing request +// +// Everything else in here is mostly plumbing, metrics and debug. +// +// +// The Work Queue +// +// The two central classes are LLTextureFetch and LLTextureFetchWorker. +// LLTextureFetch combines threading with a priority queue of work +// requests. The priority queue is sorted by a U32 priority derived +// from the F32 priority in the APIs. The *only* work request that +// receives service time by this thread is the highest priority +// request. All others wait until it is complete or a dynamic priority +// change has re-ordered work. +// +// LLTextureFetchWorker implements the work request and is 1:1 with +// texture fetch requests. Embedded in each is a state machine that +// walks it through the cache, HTTP, UDP, image decode and retry +// steps of texture acquisition. +// // // Threads // @@ -83,6 +127,7 @@ // 5. Tid Image decoder's worker thread // 6. Thl HTTP library's worker thread // +// // Mutexes/Condition Variables // // 1. Mt Mutex defined for LLThread's condition variable (base class of @@ -98,6 +143,7 @@ // LLTextureFetchWorker). One per request. // 7. Mw LLTextureFetchWorker's mutex. One per request. // +// // Lock Ordering Rules // // Not an exhaustive list but shows the order of lock acquisition @@ -105,6 +151,8 @@ // acquiring 'B'. // // 1. Mw < Mfnq +// (there are many more...) +// // // Method and Member Definitions // @@ -124,7 +172,10 @@ // comment can mean the member is unlocked or that I didn't bother // to do the archaeology. In the case of LLTextureFetchWorker, // most data members added by the leaf class are actually covered -// by the Mw lock. +// by the Mw lock. You may also see "// T" which means that +// the member's usage is restricted to one thread (except for +// perhaps construction and destruction) and so explicit locking +// isn't used. // // In code, a trailing comment like "// [-+]M" indicates a // lock acquision or release point. @@ -132,27 +183,54 @@ // // Worker Lifecycle // -// (Can't unilaterally delete, cleanup is two-phase, etc.) +// The threading and responder model makes it very likely that +// other components are holding on to a pointer to a worker request. +// So, uncoordinated deletions of requests is a guarantee of memory +// corruption in a short time. So destroying a request involves +// invocations's of LLQueuedThread/LLWorkerThread's abort/stop +// logic that removes workers and puts them ona delete queue for +// 2-phase destruction. That second phase is deferrable by calls +// to deleteOK() which only allow final destruction (via dtor) +// once deleteOK has determined that the request is in a safe +// state. +// // // Worker State Machine // // (ASCII art needed) // +// // Priority Scheme // // [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state // [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event -// [PRIORITY_HIGH, PRIORITY_URGENT) - rapidly transitioning through states, +// [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, +// rapidly transitioning through states, // no waiting allowed // +// By itself, the above work queue model would fail the concurrency +// and liveness requirements of the interface. A high priority +// request could find itself on the head and stalled for external +// reasons (see VWR-28996). So a few additional constraints are +// required to keep things running: +// * Anything that can make forward progress must be kept at a +// higher priority than anything that can't. +// * On completion of external events, the associated request +// needs to be elevated beyond the normal range to handle +// any data delivery and release any external resource. +// +// This effort is made to keep higher-priority entities moving +// forward in their state machines at every possible step of +// processing. It's not entirely proven that this produces the +// experiencial benefits promised. // ////////////////////////////////////////////////////////////////////////////// // Tuning/Parameterization Constants -static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; -static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill ////////////////////////////////////////////////////////////////////////////// @@ -425,7 +503,6 @@ private: BOOL mInLocalCache; bool mCanUseHTTP ; bool mCanUseNET ; //can get from asset server. - S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; LLCore::HttpStatus mGetStatus; @@ -745,7 +822,6 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHaveAllData(FALSE), mInLocalCache(FALSE), mCanUseHTTP(true), - mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), mWorkMutex(NULL), @@ -936,6 +1012,9 @@ void LLTextureFetchWorker::startWork(S32 param) // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); + // Release waiters while we aren't holding the Mw lock. mFetcher->releaseHttpWaiters(); @@ -1286,7 +1365,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); - ++mHTTPFailCount; return true; // failed } @@ -1313,10 +1391,8 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { - S32 max_attempts; - if (mGetStatus == LLCore::HttpStatus(HTTP_NOT_FOUND, LLCore::HE_REPLY_ERROR)) + if (http_not_found == mGetStatus) { - mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; // roll back to try UDP @@ -1328,47 +1404,32 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } } - else if (mGetStatus == LLCore::HttpStatus(HTTP_SERVICE_UNAVAILABLE, LLCore::HE_REPLY_ERROR)) + else if (http_service_unavail == mGetStatus) { - // *TODO: Should probably introduce a timer here to delay future HTTP requsts - // for a short time (~1s) to ease server load? Ideally the server would queue - // requests instead of returning 503... we already limit the number pending. - ++mHTTPFailCount; - max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } else { - const S32 HTTP_MAX_RETRY_COUNT = 3; - max_attempts = HTTP_MAX_RETRY_COUNT + 1; - ++mHTTPFailCount; llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus.toHex() << " Reason: '" << mGetReason << "'" - << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; + // *FIXME: Add retry info for reporting purposes... + // << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts + << llendl; } - if (mHTTPFailCount >= max_attempts) - { - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - mState = DECODE_IMAGE; - return false; - } - else - { - resetFormattedData(); - mState = DONE; - return true; // failed - } - } - else + if (cur_size > 0) { - mState = SEND_HTTP_REQ; - return false; // retry + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + mState = DECODE_IMAGE; + return false; } + + // Fail harder + resetFormattedData(); + mState = DONE; + return true; // failed } if (! mHttpBufferArray || ! mHttpBufferArray->size()) @@ -1649,7 +1710,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe } else { - static const LLCore::HttpStatus par_status(LLCore::HttpStatus(HTTP_PARTIAL_CONTENT, LLCore::HE_SUCCESS)); + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); partial = (par_status == status); } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 53b0f7885f..4ee13d171e 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -308,9 +308,9 @@ private: // Interfaces and objects into the core http library used // to make our HTTP requests. These replace the various // LLCurl interfaces used in the past. - LLCore::HttpRequest * mHttpRequest; - LLCore::HttpOptions * mHttpOptions; - LLCore::HttpHeaders * mHttpHeaders; + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq -- cgit v1.2.3 From a99dfeb58bd78fcd8cbc9ab39bc6eea725f22906 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 8 Jun 2012 19:21:48 -0700 Subject: SH-3145 : Expose update fetch and update priorities magic numbers in settings.xml --- indra/newview/app_settings/settings.xml | 66 +++++++++++++++++++++++++++++++++ indra/newview/llviewertexturelist.cpp | 66 +++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 27 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 47f37d7d3e..5fe51c97c5 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10752,6 +10752,72 @@ Value 0 + TextureFetchUpdateHighPriority + + Comment + Number of high priority textures to update per frame + Persist + 1 + Type + S32 + Value + 32 + + TextureFetchUpdateMaxMediumPriority + + Comment + Maximum number of medium priority textures to update per frame + Persist + 1 + Type + S32 + Value + 256 + + TextureFetchUpdateMinMediumPriority + + Comment + Minimum number of medium priority textures to update per frame + Persist + 1 + Type + S32 + Value + 32 + + TextureFetchUpdatePriorityThreshold + + Comment + Threshold under which textures will be considered too low priority and skipped for update + Persist + 1 + Type + F32 + Value + 0.0 + + TextureFetchUpdateSkipLowPriority + + Comment + Flag indicating if we want to skip textures with too low of a priority + Persist + 1 + Type + Boolean + Value + 0 + + TextureFetchUpdatePriorities + + Comment + Number of priority texture to update per frame + Persist + 1 + Type + S32 + Value + 32 + TextureLoadFullRes Comment diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index c8082d60bd..42d8cb9272 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -693,10 +693,11 @@ void LLViewerTextureList::updateImagesDecodePriorities() { // Update the decode priority for N images each frame { - const size_t max_update_count = llmin((S32) (1024*gFrameIntervalSeconds) + 1, 32); //target 1024 textures per second - S32 update_counter = llmin(max_update_count, mUUIDMap.size()/10); + static const S32 MAX_PRIO_UPDATES = gSavedSettings.getS32("TextureFetchUpdatePriorities"); // default: 32 + const size_t max_update_count = llmin((S32) (MAX_PRIO_UPDATES*MAX_PRIO_UPDATES*gFrameIntervalSeconds) + 1, MAX_PRIO_UPDATES); + S32 update_counter = llmin(max_update_count, mUUIDMap.size()); uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID); - while(update_counter > 0 && !mUUIDMap.empty()) + while ((update_counter-- > 0) && !mUUIDMap.empty()) { if (iter == mUUIDMap.end()) { @@ -704,7 +705,7 @@ void LLViewerTextureList::updateImagesDecodePriorities() } mLastUpdateUUID = iter->first; LLPointer imagep = iter->second; - ++iter; // safe to incrament now + ++iter; // safe to increment now if(imagep->isInDebug()) { @@ -784,7 +785,6 @@ void LLViewerTextureList::updateImagesDecodePriorities() imagep->setDecodePriority(decode_priority); mImageList.insert(imagep); } - update_counter--; } } } @@ -887,15 +887,24 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { LLTimer image_op_timer; - // Update the decode priority for N images each frame - // Make a list with 32 high priority entries + 256 cycled entries - const size_t max_priority_count = llmin((S32) (256*10.f*gFrameIntervalSeconds)+1, 32); - const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256); + // Update fetch for N images each frame + static const S32 MAX_HIGH_PRIO_COUNT = gSavedSettings.getS32("TextureFetchUpdateHighPriority"); // default: 32 + static const S32 MAX_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMaxMediumPriority"); // default: 256 + static const S32 MIN_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMinMediumPriority"); // default: 32 + static const F32 MIN_PRIORITY_THRESHOLD = gSavedSettings.getF32("TextureFetchUpdatePriorityThreshold"); // default: 0.0 + static const bool SKIP_LOW_PRIO = gSavedSettings.getBOOL("TextureFetchUpdateSkipLowPriority"); // default: false + + size_t max_priority_count = llmin((S32) (MAX_HIGH_PRIO_COUNT*MAX_HIGH_PRIO_COUNT*gFrameIntervalSeconds)+1, MAX_HIGH_PRIO_COUNT); + max_priority_count = llmin(max_priority_count, mImageList.size()); + + size_t total_update_count = mUUIDMap.size(); + size_t max_update_count = llmin((S32) (MAX_UPDATE_COUNT*MAX_UPDATE_COUNT*gFrameIntervalSeconds)+1, MAX_UPDATE_COUNT); + max_update_count = llmin(max_update_count, total_update_count); - // 32 high priority entries + // MAX_HIGH_PRIO_COUNT high priority entries typedef std::vector entries_list_t; entries_list_t entries; - size_t update_counter = llmin(max_priority_count, mImageList.size()); + size_t update_counter = max_priority_count; image_priority_list_t::iterator iter1 = mImageList.begin(); while(update_counter > 0) { @@ -905,43 +914,46 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) update_counter--; } - // 256 cycled entries - update_counter = llmin(max_update_count, mUUIDMap.size()); + // MAX_UPDATE_COUNT cycled entries + update_counter = max_update_count; if(update_counter > 0) { uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); - uuid_map_t::iterator iter2p = iter2; - while(update_counter > 0) + while ((update_counter > 0) && (total_update_count > 0)) { if (iter2 == mUUIDMap.end()) { iter2 = mUUIDMap.begin(); } - entries.push_back(iter2->second); - iter2p = iter2++; - update_counter--; + LLViewerFetchedTexture* imagep = iter2->second; + // Skip the textures where there's really nothing to do so to give some times to others. Also skip the texture if it's already in the high prio set. + if (!SKIP_LOW_PRIO || (SKIP_LOW_PRIO && ((imagep->getDecodePriority() > MIN_PRIORITY_THRESHOLD) || imagep->hasFetcher()))) + { + entries.push_back(imagep); + update_counter--; + } + + iter2++; + total_update_count--; } - - mLastFetchUUID = iter2p->first; } S32 fetch_count = 0; - S32 min_count = max_priority_count + max_update_count/4; + size_t min_update_count = llmin(MIN_UPDATE_COUNT,(S32)(entries.size()-max_priority_count)); + S32 min_count = max_priority_count + min_update_count; for (entries_list_t::iterator iter3 = entries.begin(); iter3 != entries.end(); ) { LLViewerFetchedTexture* imagep = *iter3++; - - bool fetching = imagep->updateFetch(); - if (fetching) + fetch_count += (imagep->updateFetch() ? 1 : 0); + if (min_count <= min_update_count) { - fetch_count++; + mLastFetchUUID = imagep->getID(); } - if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time) + if ((min_count-- <= 0) && (image_op_timer.getElapsedTimeF32() > max_time)) { break; } - min_count--; } //if (fetch_count == 0) //{ -- cgit v1.2.3 From 08c424e5111f4fae34f55935739fd475c8ab3bff Mon Sep 17 00:00:00 2001 From: MartinRJ Fayray Date: Sun, 10 Jun 2012 14:18:38 +0200 Subject: STORM-1878: Blocking an avatar does not derender worn lights --- indra/newview/llmutelist.cpp | 25 +++++++++++++++++++++++++ indra/newview/pipeline.cpp | 15 +++++++++++++++ indra/newview/pipeline.h | 2 ++ 3 files changed, 42 insertions(+) diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index a7059eb519..54522bb7f6 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -44,6 +44,8 @@ #include "llmutelist.h" +#include "pipeline.h" + #include #include "lldispatcher.h" @@ -192,6 +194,23 @@ BOOL LLMuteList::isLinden(const std::string& name) const return last_name == "Linden"; } +static LLVOAvatar* find_avatar(const LLUUID& id) +{ + LLViewerObject *obj = gObjectList.findObject(id); + while (obj && obj->isAttachment()) + { + obj = (LLViewerObject *)obj->getParent(); + } + + if (obj && obj->isAvatar()) + { + return (LLVOAvatar*)obj; + } + else + { + return NULL; + } +} BOOL LLMuteList::add(const LLMute& mute, U32 flags) { @@ -288,6 +307,12 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) LLViewerPartSim::getInstance()->clearParticlesByOwnerID(localmute.mID); } } + //mute local lights that are attached to the avatar + LLVOAvatar *avatarp = find_avatar(localmute.mID); + if (avatarp) + { + LLPipeline::removeMutedAVsLights(avatarp); + } return TRUE; } } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index ab994c71cb..7b0677ea1e 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1635,6 +1635,21 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable) } +//static +void LLPipeline::removeMutedAVsLights(LLVOAvatar* muted_avatar) +{ + LLFastTimer t(FTM_REMOVE_FROM_LIGHT_SET); + for (light_set_t::iterator iter = gPipeline.mNearbyLights.begin(); + iter != gPipeline.mNearbyLights.end(); iter++) + { + if (iter->drawable->getVObj()->isAttachment() && iter->drawable->getVObj()->getAvatar() == muted_avatar) + { + gPipeline.mLights.erase(iter->drawable); + gPipeline.mNearbyLights.erase(iter); + } + } +} + U32 LLPipeline::addObject(LLViewerObject *vobj) { LLMemType mt_ao(LLMemType::MTYPE_PIPELINE_ADD_OBJECT); diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index b8b4f164fe..cabb359884 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -151,6 +151,8 @@ public: void unlinkDrawable(LLDrawable*); + static void removeMutedAVsLights(LLVOAvatar*); + // Object related methods void markVisible(LLDrawable *drawablep, LLCamera& camera); void markOccluder(LLSpatialGroup* group); -- cgit v1.2.3 From 5700ba737d23c02bfa61010c194f9e70ff6e75dd Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 11 Jun 2012 11:00:38 -0400 Subject: avatar metrics - cleanup, removed unused fields --- indra/newview/llstartup.cpp | 14 ++++++++++++-- indra/newview/lltexturefetch.cpp | 2 +- indra/newview/llviewerstats.cpp | 9 +-------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6b0fc26db7..ebde7a55fe 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -361,6 +361,15 @@ bool idle_startup() if ( STATE_FIRST == LLStartUp::getStartupState() ) { + static bool first_call = true; + if (first_call) + { + // Other phases get handled when startup state changes, + // need to capture the initial state as well. + LLStartUp::getPhases().startPhase(LLStartUp::getStartupStateString()); + first_call = false; + } + gViewerWindow->showCursor(); gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -2707,9 +2716,10 @@ void LLStartUp::setStartupState( EStartupState state ) getStartupStateString() << " to " << startupStateToString(state) << LL_ENDL; - sPhases->stopPhase(getStartupStateString()); + getPhases().stopPhase(getStartupStateString()); gStartupState = state; - sPhases->startPhase(getStartupStateString()); + getPhases().startPhase(getStartupStateString()); + postStartupState(); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 0c2d813e2c..f47f3864dd 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1906,7 +1906,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), mOriginFetchSource(LLTextureFetch::FROM_ALL), - mFetcherLocked(FALSE) + mFetcherLocked(FALSE) { mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 497e95c5e3..71913a71d0 100755 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -927,15 +927,8 @@ LLSD LLViewerStats::PhaseMap::dumpPhases() for (phase_map_t::iterator iter = mPhaseMap.begin(); iter != mPhaseMap.end(); ++iter) { const std::string& phase_name = iter->first; - result[phase_name]["completed"] = !(iter->second.getStarted()); + result[phase_name]["completed"] = LLSD::Integer(!(iter->second.getStarted())); result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32(); -#if 0 // global stats for each phase seem like overkill here - phase_stats_t::iterator stats_iter = sPhaseStats.find(phase_name); - if (stats_iter != sPhaseStats.end()) - { - result[phase_name]["stats"] = stats_iter->second.getData(); - } -#endif } return result; } -- cgit v1.2.3 From e8bec251dc98950958be2f4f668c5408fcf56b9b Mon Sep 17 00:00:00 2001 From: Tank_Master Date: Mon, 11 Jun 2012 08:36:10 -0700 Subject: remove tooltip when hovering over main volume icon in the status bar --- indra/newview/skins/default/xui/en/panel_status_bar.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index 3aa34439f1..dd2a0c6627 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -129,6 +129,5 @@ left_pad="5" top="2" name="volume_btn" - tool_tip="Global Volume Control" width="16" /> -- cgit v1.2.3 From f4a59854c5aab0fb1f666d8df45984a0f4cfd465 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 12:24:54 -0400 Subject: Unit test fixups. Mac/Linux detect memory leak due to llwarns, try/catch cleanup. Our logging holds on to a changing bit of memory between operations and the memory leak detection I'm using senses this and complains. So, for now, disable the final memory check on Mac & Linux, leave it active on Windows. Solve this for real some other day. Add try/catch blocks to do cleanup in unit tests that go wrong so that we don't get a cascade of assertion failures when subsequent tests find singletons still alive. --- indra/llcorehttp/_httplibcurl.cpp | 2 +- indra/llcorehttp/tests/llcorehttp_test.cpp | 9 - indra/llcorehttp/tests/test_httprequest.hpp | 496 ++++++++++++++++------------ 3 files changed, 279 insertions(+), 228 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 05b2c2be69..332b6f3856 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -242,7 +242,7 @@ HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." << LL_ENDL; } - + op->stageFromActive(mService); op->release(); return HttpService::REQUEST_SLEEP; diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 0ee767fdca..2d48bca443 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -130,12 +130,3 @@ void ssl_locking_callback(int mode, int type, const char * /* file */, int /* li } -#if defined(WIN32) - -int getopt(int argc, char * const argv[], const char *optstring) -{ - return -1; -} - -#endif - diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 2d91b95347..68da9e2dc7 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -110,24 +110,35 @@ void HttpRequestTestObjectType::test<1>() set_test_name("HttpRequest construction"); + HttpRequest * req = NULL; + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); - // Get singletons created - HttpRequest::createService(); + try + { + // Get singletons created + HttpRequest::createService(); - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - HttpRequest::destroyService(); + HttpRequest::destroyService(); - // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -137,35 +148,46 @@ void HttpRequestTestObjectType::test<2>() set_test_name("HttpRequest and Null Op queued"); + HttpRequest * req = NULL; + // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); - // Get singletons created - HttpRequest::createService(); + try + { + // Get singletons created + HttpRequest::createService(); - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure(mMemTotal < GetMemTotal()); - // Issue a NoOp - HttpHandle handle = req->requestNoOp(NULL); - ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure(handle != LLCORE_HTTP_HANDLE_INVALID); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - // We're still holding onto the operation which is - // sitting, unserviced, on the request queue so... - ensure(mMemTotal < GetMemTotal()); + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure(mMemTotal < GetMemTotal()); - // Request queue should have two references: global singleton & service object - ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); - // Okay, tear it down - HttpRequest::destroyService(); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure(mMemTotal == GetMemTotal()); + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure(mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } @@ -173,7 +195,7 @@ template <> template <> void HttpRequestTestObjectType::test<3>() { ScopedCurlInit ready; - + set_test_name("HttpRequest NoOp + Stop execution"); // Handler can be stack-allocated *if* there are no dangling @@ -184,69 +206,79 @@ void HttpRequestTestObjectType::test<3>() // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { - // Get singletons created - HttpRequest::createService(); + // Get singletons created + HttpRequest::createService(); - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue a NoOp - HttpHandle handle = req->requestNoOp(&handler); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(20); - while (count++ < limit && mHandlerCalls < 1) - { - req->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 1); + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); - // Okay, request a shutdown of the servicing thread - handle = req->requestStopThread(&handler); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 2) - { - req->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 2); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); - // release the request object - delete req; - req = NULL; + // release the request object + delete req; + req = NULL; - // Shut down service - HttpRequest::destroyService(); + // Shut down service + HttpRequest::destroyService(); - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -264,80 +296,92 @@ void HttpRequestTestObjectType::test<4>() // record the total amount of dynamically allocated memory mMemTotal = GetMemTotal(); mHandlerCalls = 0; + + HttpRequest * req1 = NULL; + HttpRequest * req2 = NULL; + + try + { - // Get singletons created - HttpRequest::createService(); + // Get singletons created + HttpRequest::createService(); - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req1 = new HttpRequest(); - HttpRequest * req2 = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // Issue some NoOps - HttpHandle handle = req1->requestNoOp(&handler1); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler1.mExpectHandle = handle; - - handle = req2->requestNoOp(&handler2); - ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler2.mExpectHandle = handle; - - // Run the notification pump. - int count(0); - int limit(20); - while (count++ < limit && mHandlerCalls < 2) - { - req1->update(1000); - req2->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 2); + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req1 = new HttpRequest(); + req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); - // Okay, request a shutdown of the servicing thread - handle = req2->requestStopThread(&handler2); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - handler2.mExpectHandle = handle; + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 3) - { - req1->update(1000); - req2->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 3); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000); + req2->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); - // release the request object - delete req1; - req1 = NULL; - delete req2; - req2 = NULL; - - // Shut down service - HttpRequest::destroyService(); + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 3 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req1; + delete req2; + HttpRequest::destroyService(); + throw; + } } template <> template <> @@ -356,77 +400,93 @@ void HttpRequestTestObjectType::test<5>() mMemTotal = GetMemTotal(); mHandlerCalls = 0; - // Get singletons created - HttpRequest::createService(); - - // Start threading early so that thread memory is invariant - // over the test. - HttpRequest::startThread(); - - // create a new ref counted object with an implicit reference - HttpRequest * req = new HttpRequest(); - ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); - - // 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, - NULL, - NULL, - &handler); - ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); - - // Run the notification pump. - int count(0); - int limit(180); // With retries, can take more than 10 seconds to give up - while (count++ < limit && mHandlerCalls < 1) + HttpRequest * req = NULL; + + try { - req->update(1000); - usleep(100000); - } - ensure("Request executed in reasonable time", count < limit); - ensure("One handler invocation for request", mHandlerCalls == 1); + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // 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, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(180); // With retries, can take more than 10 seconds to give up + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); - // Okay, request a shutdown of the servicing thread - mStatus = HttpStatus(); - handle = req->requestStopThread(&handler); - ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); - // Run the notification pump again - count = 0; - limit = 100; - while (count++ < limit && mHandlerCalls < 2) - { - req->update(1000); - usleep(100000); - } - ensure("Second request executed in reasonable time", count < limit); - ensure("Second handler invocation", mHandlerCalls == 2); + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); - // See that we actually shutdown the thread - count = 0; - limit = 10; - while (count++ < limit && ! HttpService::isStopped()) - { - usleep(100000); - } - ensure("Thread actually stopped running", HttpService::isStopped()); + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); - // release the request object - delete req; - req = NULL; + ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // Shut down service - HttpRequest::destroyService(); +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... - // We have left over state so can't really say something - // definitive about memory usage at the end of this. - ensure("Two handler calls on the way out", 2 == mHandlerCalls); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } } } // end namespace tut -- cgit v1.2.3 From 070c72263173d70c3bd17c454ede790ea4091245 Mon Sep 17 00:00:00 2001 From: Kadah_Coba Date: Mon, 11 Jun 2012 12:20:09 -0700 Subject: storm-1843: remove unused ParcelMediaURLFilter code --- etc/message.xml | 8 ----- indra/llinventory/llparcel.cpp | 67 ------------------------------------------ indra/llinventory/llparcel.h | 7 ----- 3 files changed, 82 deletions(-) diff --git a/etc/message.xml b/etc/message.xml index 3445975545..3b5a4a428e 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -385,14 +385,6 @@ trusted-sender true - - ParcelMediaURLFilter - - flavor - llsd - trusted-sender - false - ParcelNavigateMedia diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 433076c7a9..a871bcbb25 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -193,8 +193,6 @@ void LLParcel::init(const LLUUID &owner_id, mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); - mMediaURLFilterEnable = FALSE; - mMediaURLFilterList = LLSD::emptyArray(); mMediaAllowNavigate = TRUE; mMediaURLTimeout = 0.0f; mMediaPreventCameraZoom = FALSE; @@ -336,38 +334,6 @@ void LLParcel::setMediaURLResetTimer(F32 time) mMediaResetTimer.setTimerExpirySec(time); } -void LLParcel::setMediaURLFilterList(LLSD list) -{ - // sanity check LLSD - // must be array of strings - if (!list.isArray()) - { - return; - } - - for (S32 i = 0; i < list.size(); i++) - { - if (!list[i].isString()) - return; - } - - // can't be too big - const S32 MAX_SIZE = 50; - if (list.size() > MAX_SIZE) - { - LLSD new_list = LLSD::emptyArray(); - - for (S32 i = 0; i < llmin(list.size(), MAX_SIZE); i++) - { - new_list.append(list[i]); - } - - list = new_list; - } - - mMediaURLFilterList = list; -} - // virtual void LLParcel::setLocalID(S32 local_id) { @@ -622,34 +588,6 @@ BOOL LLParcel::importAccessEntry(std::istream& input_stream, LLAccessEntry* entr return input_stream.good(); } -BOOL LLParcel::importMediaURLFilter(std::istream& input_stream, std::string& url) -{ - skip_to_end_of_next_keyword("{", input_stream); - - while(input_stream.good()) - { - skip_comments_and_emptyspace(input_stream); - std::string line, keyword, value; - get_line(line, input_stream, MAX_STRING); - get_keyword_and_value(keyword, value, line); - - if ("}" == keyword) - { - break; - } - else if ("url" == keyword) - { - url = value; - } - else - { - llwarns << "Unknown keyword in parcel media url filter section: <" - << keyword << ">" << llendl; - } - } - return input_stream.good(); -} - // Assumes we are in a block "ParcelData" void LLParcel::packMessage(LLMessageSystem* msg) { @@ -696,8 +634,6 @@ void LLParcel::packMessage(LLSD& msg) msg["media_allow_navigate"] = getMediaAllowNavigate(); msg["media_prevent_camera_zoom"] = getMediaPreventCameraZoom(); msg["media_url_timeout"] = getMediaURLTimeout(); - msg["media_url_filter_enable"] = getMediaURLFilterEnable(); - msg["media_url_filter_list"] = getMediaURLFilterList(); msg["group_id"] = getGroupID(); msg["pass_price"] = mPassPrice; msg["pass_hours"] = mPassHours; @@ -789,7 +725,6 @@ void LLParcel::unpackMessage(LLMessageSystem* msg) msg->getString("MediaLinkSharing", "MediaCurrentURL", buffer); setMediaCurrentURL(buffer); msg->getU8 ( "MediaLinkSharing", "MediaAllowNavigate", mMediaAllowNavigate ); - msg->getU8 ( "MediaLinkSharing", "MediaURLFilterEnable", mMediaURLFilterEnable ); msg->getU8 ( "MediaLinkSharing", "MediaPreventCameraZoom", mMediaPreventCameraZoom ); msg->getF32( "MediaLinkSharing", "MediaURLTimeout", mMediaURLTimeout); } @@ -1250,8 +1185,6 @@ void LLParcel::clearParcel() mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); - setMediaURLFilterList(LLSD::emptyArray()); - setMediaURLFilterEnable(FALSE); setMediaAllowNavigate(TRUE); setMediaPreventCameraZoom(FALSE); setMediaURLTimeout(0.0f); diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index e36d0b20d2..7d7615bf51 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -247,8 +247,6 @@ public: void setMediaWidth(S32 width); void setMediaHeight(S32 height); void setMediaCurrentURL(const std::string& url); - void setMediaURLFilterEnable(U8 enable) { mMediaURLFilterEnable = enable; } - void setMediaURLFilterList(LLSD list); void setMediaAllowNavigate(U8 enable) { mMediaAllowNavigate = enable; } void setMediaURLTimeout(F32 timeout) { mMediaURLTimeout = timeout; } void setMediaPreventCameraZoom(U8 enable) { mMediaPreventCameraZoom = enable; } @@ -310,7 +308,6 @@ public: // BOOL importStream(std::istream& input_stream); BOOL importAccessEntry(std::istream& input_stream, LLAccessEntry* entry); - BOOL importMediaURLFilter(std::istream& input_stream, std::string& url); // BOOL exportStream(std::ostream& output_stream); void packMessage(LLMessageSystem* msg); @@ -354,8 +351,6 @@ public: U8 getMediaAutoScale() const { return mMediaAutoScale; } U8 getMediaLoop() const { return mMediaLoop; } const std::string& getMediaCurrentURL() const { return mMediaCurrentURL; } - U8 getMediaURLFilterEnable() const { return mMediaURLFilterEnable; } - LLSD getMediaURLFilterList() const { return mMediaURLFilterList; } U8 getMediaAllowNavigate() const { return mMediaAllowNavigate; } F32 getMediaURLTimeout() const { return mMediaURLTimeout; } U8 getMediaPreventCameraZoom() const { return mMediaPreventCameraZoom; } @@ -651,8 +646,6 @@ protected: U8 mMediaLoop; std::string mMediaCurrentURL; LLUUID mMediaID; - U8 mMediaURLFilterEnable; - LLSD mMediaURLFilterList; U8 mMediaAllowNavigate; U8 mMediaPreventCameraZoom; F32 mMediaURLTimeout; -- cgit v1.2.3 From 89187229dd630845177ecd7a16e2b9cb01dc47ce Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 15:28:06 -0400 Subject: Refactoring of the request completion thread and removal of 206/content-range hack in xport. Retry/response handling is decided in policy so moved that there. Removed special case 206-without-content-range response in transport. Have this sitation recognizable in the API and let callers deal with it as needed. --- indra/llcorehttp/_httplibcurl.cpp | 51 +++++++------------------------------ indra/llcorehttp/_httplibcurl.h | 4 +-- indra/llcorehttp/_httpoprequest.cpp | 19 +++----------- indra/llcorehttp/_httppolicy.cpp | 42 ++++++++++++++++++++++++++++++ indra/llcorehttp/_httppolicy.h | 10 ++++++++ indra/newview/lltexturefetch.cpp | 9 +++++-- 6 files changed, 72 insertions(+), 63 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 332b6f3856..e134a28401 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -113,8 +113,11 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() CURL * handle(msg->easy_handle); CURLcode result(msg->data.result); - HttpService::ELoopSpeed speed(completeRequest(mMultiHandles[policy_class], handle, result)); - ret = (std::min)(ret, speed); + if (completeRequest(mMultiHandles[policy_class], handle, result)) + { + // Request is still active, don't get too sleepy + ret = (std::min)(ret, HttpService::NORMAL); + } handle = NULL; // No longer valid on return } else if (CURLMSG_NONE == msg->msg) @@ -161,12 +164,8 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } -HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { - static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); - static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); - static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); - HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); // *FIXME: check the pointer @@ -209,43 +208,11 @@ HttpService::ELoopSpeed HttpLibcurl::completeRequest(CURLM * multi_handle, CURL curl_multi_remove_handle(multi_handle, handle); curl_easy_cleanup(handle); op->mCurlHandle = NULL; - - // Retry or finalize - if (! op->mStatus) - { - // If this failed, we might want to retry. Have to inspect - // the status a little more deeply for those reasons worth retrying... - if (op->mPolicyRetries < op->mPolicyRetryLimit && - ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || - cant_connect == op->mStatus || - cant_res_proxy == op->mStatus || - cant_res_host == op->mStatus)) - { - // Okay, worth a retry. We include 499 in this test as - // it's the old 'who knows?' error from many grid services... - HttpPolicy & policy(mService->getPolicy()); - - policy.retryOp(op); - return HttpService::NORMAL; // Having pushed to retry, keep things running - } - } - // This op is done, finalize it delivering it to the reply queue... - if (! op->mStatus) - { - LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries - << " retries. Reason: " << op->mStatus.toString() - << LL_ENDL; - } - else if (op->mPolicyRetries) - { - LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; - } + HttpPolicy & policy(mService->getPolicy()); + bool still_active(policy.stageAfterCompletion(op)); - op->stageFromActive(mService); - op->release(); - return HttpService::REQUEST_SLEEP; + return still_active; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index fe628b9ab0..16b68bde43 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -83,9 +83,7 @@ public: protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. - HttpService::ELoopSpeed completeRequest(CURLM * multi_handle, - CURL * handle, - CURLcode status); + bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 895629c514..ea0b99303e 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -144,6 +144,8 @@ HttpOpRequest::~HttpOpRequest() mCurlHeaders = NULL; } + mReplyOffset = 0; + mReplyLength = 0; if (mReplyBody) { mReplyBody->release(); @@ -211,26 +213,11 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setStatus(mStatus); response->setBody(mReplyBody); response->setHeaders(mReplyHeaders); - unsigned int offset(0), length(0); if (mReplyOffset || mReplyLength) { // Got an explicit offset/length in response - offset = mReplyOffset; - length = mReplyLength; - } - else if (mReplyBody && partial_content == mStatus) - { - // Legacy grid services did not provide a 'Content-Range' - // header in responses to full- or partly-satisfyiable - // 'Range' requests. For these, we have to hope that - // the data starts where requested and the length is simply - // whatever we received. A bit of sanity could be provided - // by overlapping ranged requests and verifying that the - // overlap matches. - offset = mReqOffset; - length = mReplyBody->size(); + response->setRange(mReplyOffset, mReplyLength); } - response->setRange(offset, length); mLibraryHandler->onCompleted(static_cast(this), response); diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1f4cd34a4b..72bb6f14e4 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -185,5 +185,47 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior return false; } +bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) +{ + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + retryOp(op); + return true; // still active/ready + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; + } + + op->stageFromActive(mService); + op->release(); + return false; // not active +} + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 6f18264f3d..14f6a9a676 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -79,6 +79,16 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// When transport is finished with an op and takes it off the + /// active queue, it is delivered here for dispatch. Policy + /// may send it back to the ready/retry queues if it needs another + /// go or we may finalize it and send it on to the reply queue. + /// + /// @return Returns true of the request is still active + /// or ready after staging, false if has been + /// sent on to the reply queue. + bool stageAfterCompletion(HttpOpRequest * op); + // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. HttpPolicyGlobal & getGlobalOptions() diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4a46ea0e97..571f9ab3b5 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1692,8 +1692,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe << " status: " << status.toHex() << " '" << status.toString() << "'" << llendl; - unsigned int offset(0), length(0); - response->getRange(&offset, &length); +// unsigned int offset(0), length(0); +// response->getRange(&offset, &length); // llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle // << " status: " << status.toULong() << " '" << status.toString() << "'" // << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize @@ -1710,6 +1710,11 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe } else { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); partial = (par_status == status); -- cgit v1.2.3 From 76abab0618861a6c97c60e6184af5506891f2e9d Mon Sep 17 00:00:00 2001 From: Tank_Master Date: Mon, 11 Jun 2012 13:19:15 -0700 Subject: When using OS autobuild config switches, set build configuration in VS to specified type (release, relwithdebinfo, or debug) Also, set startup project to SecondLife-bin --- autobuild.xml | 36 ++++++++++++++++++++++++++++++++++++ doc/contributions.txt | 1 + 2 files changed, 37 insertions(+) diff --git a/autobuild.xml b/autobuild.xml index 0e4b81324a..fb34117c0c 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2303,6 +2303,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + Debug + --startup + secondlife-bin + options -G @@ -2379,6 +2391,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + RelWithDebInfo + --startup + secondlife-bin + options -G @@ -2455,6 +2479,18 @@ configure + arguments + + ..\indra + && + ..\indra\tools\vstool\VSTool.exe + --solution + SecondLife.sln + --config + Release + --startup + secondlife-bin + options -G diff --git a/doc/contributions.txt b/doc/contributions.txt index 04edfb4bea..46b451291c 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -1091,6 +1091,7 @@ Sudane Erato Synystyr Texan Takeda Terrawyng TankMaster Finesmith + OPEN-140 STORM-1100 STORM-1602 STORM-1258 -- cgit v1.2.3 From 267ab5b417eaef64a170d69ad83334df9d566ed9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 17:47:04 -0400 Subject: Convert BufferArray interfaces to void * (not char *). HttpRequest::update() honor time limit. Generally, opaque data operations are expected to be over 'void *' and have now converted interfaces to do that. Update() method honors millisecond limit to dwell time. Might want to homologate the millis/uSecs mix later.... --- indra/llcorehttp/bufferarray.cpp | 37 ++++++++++++++++------------- indra/llcorehttp/bufferarray.h | 8 +++---- indra/llcorehttp/httprequest.cpp | 13 ++++------ indra/llcorehttp/httprequest.h | 2 +- indra/llcorehttp/tests/test_bufferarray.hpp | 6 ++--- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 36a8006bce..6d5309a043 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -108,10 +108,11 @@ BufferArray::~BufferArray() } -size_t BufferArray::append(const char * src, size_t len) +size_t BufferArray::append(const void * src, size_t len) { const size_t ret(len); - + const char * c_src(static_cast(src)); + // First, try to copy into the last block if (len && ! mBlocks.empty()) { @@ -121,11 +122,11 @@ size_t BufferArray::append(const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -140,19 +141,19 @@ size_t BufferArray::append(const char * src, size_t len) mBlocks.reserve(mBlocks.size() + 5); } Block * block = Block::alloc(BLOCK_ALLOC_SIZE); - memcpy(block->mData, src, copy_len); + memcpy(block->mData, c_src, copy_len); block->mUsed = copy_len; llassert_always(block->mUsed <= block->mAlloced); mBlocks.push_back(block); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } return ret; } -char * BufferArray::appendBufferAlloc(size_t len) +void * BufferArray::appendBufferAlloc(size_t len) { // If someone asks for zero-length, we give them a valid pointer. if (mBlocks.size() >= mBlocks.capacity()) @@ -167,8 +168,10 @@ char * BufferArray::appendBufferAlloc(size_t len) } -size_t BufferArray::read(size_t pos, char * dst, size_t len) +size_t BufferArray::read(size_t pos, void * dst, size_t len) { + char * c_dst(static_cast(dst)); + if (pos >= mLen) return 0; size_t len_limit(mLen - pos); @@ -188,10 +191,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(dst, &block.mData[offset], block_len); + memcpy(c_dst, &block.mData[offset], block_len); result += block_len; len -= block_len; - dst += block_len; + c_dst += block_len; offset = 0; ++block_start; } @@ -201,8 +204,10 @@ size_t BufferArray::read(size_t pos, char * dst, size_t len) } -size_t BufferArray::write(size_t pos, const char * src, size_t len) +size_t BufferArray::write(size_t pos, const void * src, size_t len) { + const char * c_src(static_cast(src)); + if (pos > mLen || 0 == len) return 0; @@ -220,9 +225,9 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) size_t block_limit(block.mUsed - offset); size_t block_len(std::min(block_limit, len)); - memcpy(&block.mData[offset], src, block_len); + memcpy(&block.mData[offset], c_src, block_len); result += block_len; - src += block_len; + c_src += block_len; len -= block_len; offset = 0; ++block_start; @@ -240,12 +245,12 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) // Some will fit... const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); - memcpy(&last.mData[last.mUsed], src, copy_len); + memcpy(&last.mData[last.mUsed], c_src, copy_len); last.mUsed += copy_len; result += copy_len; llassert_always(last.mUsed <= last.mAlloced); mLen += copy_len; - src += copy_len; + c_src += copy_len; len -= copy_len; } } @@ -254,7 +259,7 @@ size_t BufferArray::write(size_t pos, const char * src, size_t len) { // Some or all of the remaining write data will // be an append. - result += append(src, len); + result += append(c_src, len); } return result; diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 9ccd85d4f8..72c3e1c669 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -81,7 +81,7 @@ public: /// position is one beyond the final byte of the buffer. /// /// @return Count of bytes copied to BufferArray - size_t append(const char * src, size_t len); + size_t append(const void * src, size_t len); /// Similar to @see append(), this call guarantees a /// contiguous block of memory of requested size placed @@ -91,7 +91,7 @@ public: /// /// @return Pointer to contiguous region at end /// of BufferArray of 'len' size. - char * appendBufferAlloc(size_t len); + void * appendBufferAlloc(size_t len); /// Current count of bytes in BufferArray instance. size_t size() const @@ -102,13 +102,13 @@ public: /// Copies data from the given position in the instance /// to the caller's buffer. Will return a short count of /// bytes copied if the 'len' extends beyond the data. - size_t read(size_t pos, char * dst, size_t len); + size_t read(size_t pos, void * dst, size_t len); /// Copies data from the caller's buffer to the instance /// at the current position. May overwrite existing data, /// append data when current position is equal to the /// size of the instance or do a mix of both. - size_t write(size_t pos, const char * src, size_t len); + size_t write(size_t pos, const void * src, size_t len); protected: int findBlock(size_t pos, size_t * ret_offset); diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index baa0fe1a84..2f36168f8b 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -35,6 +35,8 @@ #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "lltimer.h" + namespace { @@ -279,14 +281,9 @@ HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priori HttpStatus HttpRequest::update(long millis) { - HttpStatus status; - - // *FIXME: need timer stuff - // long now(getNow()); - // long limit(now + millis); - + const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); HttpOperation * op(NULL); - while ((op = mReplyQueue->fetchOp())) + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) { // Process operation op->visitNotifier(this); @@ -295,7 +292,7 @@ HttpStatus HttpRequest::update(long millis) op->release(); } - return status; + return HttpStatus(); } diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 3592d5c6a3..01dbfba6dd 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -259,7 +259,7 @@ public: /// @param millis Maximum number of wallclock milliseconds to /// spend in the call. As hinted at above, this /// is partly a function of application code so it's - /// a soft limit. (And not currently implemented.) + /// a soft limit. /// /// @return Standard status code. HttpStatus update(long millis); diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 2ad9391d1c..3f947db967 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -348,7 +348,7 @@ void BufferArrayTestObjectType::test<7>() ensure("Append length correct", str2_len == len); // append some more - char * out_buf(ba->appendBufferAlloc(str1_len)); + void * out_buf(ba->appendBufferAlloc(str1_len)); memcpy(out_buf, str1, str1_len); // And some final writes @@ -399,11 +399,11 @@ void BufferArrayTestObjectType::test<8>() len = ba->write(str1_len, str1, str1_len); // zero-length allocate (we allow this with a valid pointer returned) - char * out_buf(ba->appendBufferAlloc(0)); + void * out_buf(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); // Do it again - char * out_buf2(ba->appendBufferAlloc(0)); + void * out_buf2(ba->appendBufferAlloc(0)); ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); -- cgit v1.2.3 From 75242eab8f8a892c792681fca080d86cfbb3e061 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 11 Jun 2012 19:06:52 -0400 Subject: Bring in the testrunner/http server scaffold for better integration testing. This brings in a copy of llmessage's llsdmessage testing server. We run a mocked HTTP service to handle requests and the integration tests run against it by picking up the LL_TEST_PORT environment variable when running. Add some checks and output to produce useful info when run in the wrong environment and when bad status is received. Later will add a dead port as well so we can test that rather than use 'localhost:2'. --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/tests/llcorehttp_test.cpp | 36 ++++ indra/llcorehttp/tests/llcorehttp_test.h | 5 + indra/llcorehttp/tests/test_httprequest.hpp | 119 ++++++++++- indra/llcorehttp/tests/test_llcorehttp_peer.py | 146 ++++++++++++++ indra/llcorehttp/tests/testrunner.py | 262 +++++++++++++++++++++++++ 6 files changed, 568 insertions(+), 2 deletions(-) create mode 100644 indra/llcorehttp/tests/test_llcorehttp_peer.py create mode 100644 indra/llcorehttp/tests/testrunner.py diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 3fda524ddf..a0827286e3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -125,6 +125,8 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}" "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" ) endif (LL_TESTS) diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 2d48bca443..f59361ab53 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -27,6 +27,7 @@ #include "llcorehttp_test.h" #include +#include // These are not the right way in viewer for some reason: // #include @@ -130,3 +131,38 @@ void ssl_locking_callback(int mode, int type, const char * /* file */, int /* li } +std::string get_base_url() +{ + const char * env(getenv("LL_TEST_PORT")); + + if (! env) + { + std::cerr << "LL_TEST_PORT environment variable missing." << std::endl; + std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl; + tut::ensure("LL_TEST_PORT set in environment", NULL != env); + } + + int port(atoi(env)); + std::ostringstream out; + out << "http://localhost:" << port << "/"; + return out.str(); +} + + +void stop_thread(LLCore::HttpRequest * req) +{ + if (req) + { + req->requestStopThread(NULL); + + int count = 0; + int limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + req->update(1000); + usleep(100000); + } + } +} + + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h index 1550881a00..a9567435ce 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.h +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -32,6 +32,9 @@ #include #include +#include + +#include "httprequest.h" // Initialization and cleanup for libcurl. Mainly provides // a mutex callback for SSL and a thread ID hash for libcurl. @@ -40,6 +43,8 @@ // operations. extern void init_curl(); extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); class ScopedCurlInit { diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 68da9e2dc7..81f8fe4a85 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -84,8 +84,10 @@ public: if (response && mState) { const HttpStatus actual_status(response->getStatus()); - - ensure("Expected HttpStatus received in response", actual_status == mState->mStatus); + std::ostringstream test; + test << "Expected HttpStatus received in response. Wanted: " + << mState->mStatus.toHex() << " Received: " << actual_status.toHex(); + ensure(test.str().c_str(), actual_status == mState->mStatus); } if (mState) { @@ -184,6 +186,7 @@ void HttpRequestTestObjectType::test<2>() } catch (...) { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; @@ -275,6 +278,7 @@ void HttpRequestTestObjectType::test<3>() } catch (...) { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; @@ -377,6 +381,7 @@ void HttpRequestTestObjectType::test<4>() } catch (...) { + stop_thread(req1); delete req1; delete req2; HttpRequest::destroyService(); @@ -483,6 +488,116 @@ void HttpRequestTestObjectType::test<5>() } catch (...) { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); delete req; HttpRequest::destroyService(); throw; diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py new file mode 100644 index 0000000000..3e200a5c19 --- /dev/null +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +"""\ +@file test_llsdmessage_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +import os +import sys +from threading import Thread +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler + +mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +from indra.util.fastest_elementtree import parse as xml_parse +from indra.base import llsd +from testrunner import freeport, run, debug, VERBOSE + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): + """This subclass of BaseHTTPRequestHandler is to receive and echo + LLSD-flavored messages sent by the C++ LLHTTPClient. + """ + def read(self): + # The following logic is adapted from the library module + # SimpleXMLRPCServer.py. + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + try: + size_remaining = int(self.headers["content-length"]) + except (KeyError, ValueError): + return "" + max_chunk_size = 10*1024*1024 + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + chunk = self.rfile.read(chunk_size) + L.append(chunk) + size_remaining -= len(chunk) + return ''.join(L) + # end of swiped read() logic + + def read_xml(self): + # This approach reads the entire POST data into memory first + return llsd.parse(self.read()) +## # This approach attempts to stream in the LLSD XML from self.rfile, +## # assuming that the underlying XML parser reads its input file +## # incrementally. Unfortunately I haven't been able to make it work. +## tree = xml_parse(self.rfile) +## debug("Finished raw parse") +## debug("parsed XML tree %s", tree) +## debug("parsed root node %s", tree.getroot()) +## debug("root node tag %s", tree.getroot().tag) +## return llsd.to_python(tree.getroot()) + + def do_GET(self): + # Of course, don't attempt to read data. + self.answer(dict(reply="success", status=200, + reason="Your GET operation worked")) + + def do_POST(self): + # Read the provided POST data. + self.answer(self.read()) + + def answer(self, data): + debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if "fail" not in self.path: + response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) + debug("success: %s", response) + self.send_response(200) + self.send_header("Content-type", "application/llsd+xml") + self.send_header("Content-Length", str(len(response))) + self.end_headers() + self.wfile.write(response) + else: # fail requested + status = data.get("status", 500) + # self.responses maps an int status to a (short, long) pair of + # strings. We want the longer string. That's why we pass a string + # pair to get(): the [1] will select the second string, whether it + # came from self.responses or from our default pair. + reason = data.get("reason", + self.responses.get(status, + ("fail requested", + "Your request specified failure status %s " + "without providing a reason" % status))[1]) + debug("fail requested: %s: %r", status, reason) + self.send_error(status, reason) + + if not VERBOSE: + # When VERBOSE is set, skip both these overrides because they exist to + # suppress output. + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class Server(HTTPServer): + # This pernicious flag is on by default in HTTPServer. But proper + # operation of freeport() absolutely depends on it being off. + allow_reuse_address = False + +if __name__ == "__main__": + # Instantiate a Server(TestHTTPRequestHandler) on the first free port + # in the specified port range. Doing this inline is better than in a + # daemon thread: if it blows up here, we'll get a traceback. If it blew up + # in some other thread, the traceback would get eaten and we'd run the + # subject test program anyway. + httpd, port = freeport(xrange(8000, 8020), + lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + # Pass the selected port number to the subject test program via the + # environment. We don't want to impose requirements on the test program's + # command-line parsing -- and anyway, for C++ integration tests, that's + # performed in TUT code rather than our own. + os.environ["LL_TEST_PORT"] = str(port) + debug("$LL_TEST_PORT = %s", port) + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py new file mode 100644 index 0000000000..f2c841532a --- /dev/null +++ b/indra/llcorehttp/tests/testrunner.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +from __future__ import with_statement + +import os +import sys +import re +import errno +import socket + +VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "1") # default to verbose +# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if +# that construct actually turns on verbosity... +VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) + +if VERBOSE: + def debug(fmt, *args): + print fmt % args + sys.stdout.flush() +else: + debug = lambda *args: None + +def freeport(portlist, expr): + """ + Find a free server port to use. Specifically, evaluate 'expr' (a + callable(port)) until it stops raising EADDRINUSE exception. + + Pass: + + portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the + range, freeport() lets the socket.error exception propagate. If you want + unbounded, you could pass itertools.count(baseport), though of course in + practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain + the range much more sharply: if we're iterating an absurd number of times, + probably something else is wrong. + + expr: a callable accepting a port number, specifically one of the items + from portlist. If calling that callable raises socket.error with + EADDRINUSE, freeport() retrieves the next item from portlist and retries. + + Returns: (expr(port), port) + + port: the value from portlist for which expr(port) succeeded + + Raises: + + Any exception raised by expr(port) other than EADDRINUSE. + + socket.error if, for every item from portlist, expr(port) raises + socket.error. The exception you see is the one from the last item in + portlist. + + StopIteration if portlist is completely empty. + + Example: + + class Server(HTTPServer): + # If you use BaseHTTPServer.HTTPServer, turning off this flag is + # essential for proper operation of freeport()! + allow_reuse_address = False + # ... + server, port = freeport(xrange(8000, 8010), + lambda port: Server(("localhost", port), + MyRequestHandler)) + # pass 'port' to client code + # call server.serve_forever() + """ + try: + # If portlist is completely empty, let StopIteration propagate: that's an + # error because we can't return meaningful values. We have no 'port', + # therefore no 'expr(port)'. + portiter = iter(portlist) + port = portiter.next() + + while True: + try: + # If this value of port works, return as promised. + value = expr(port) + + except socket.error, err: + # Anything other than 'Address already in use', propagate + if err.args[0] != errno.EADDRINUSE: + raise + + # Here we want the next port from portiter. But on StopIteration, + # we want to raise the original exception rather than + # StopIteration. So save the original exc_info(). + type, value, tb = sys.exc_info() + try: + try: + port = portiter.next() + except StopIteration: + raise type, value, tb + finally: + # Clean up local traceback, see docs for sys.exc_info() + del tb + + else: + debug("freeport() returning %s on port %s", value, port) + return value, port + + # Recap of the control flow above: + # If expr(port) doesn't raise, return as promised. + # If expr(port) raises anything but EADDRINUSE, propagate that + # exception. + # If portiter.next() raises StopIteration -- that is, if the port + # value we just passed to expr(port) was the last available -- reraise + # the EADDRINUSE exception. + # If we've actually arrived at this point, portiter.next() delivered a + # new port value. Loop back to pass that to expr(port). + + except Exception, err: + debug("*** freeport() raising %s: %s", err.__class__.__name__, err) + raise + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...", " ".join(args)) + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s", args[0], rc) + return rc + +# **************************************************************************** +# test code -- manual at this point, see SWAT-564 +# **************************************************************************** +def test_freeport(): + # ------------------------------- Helpers -------------------------------- + from contextlib import contextmanager + # helper Context Manager for expecting an exception + # with exc(SomeError): + # raise SomeError() + # raises AssertionError otherwise. + @contextmanager + def exc(exception_class, *args): + try: + yield + except exception_class, err: + for i, expected_arg in enumerate(args): + assert expected_arg == err.args[i], \ + "Raised %s, but args[%s] is %r instead of %r" % \ + (err.__class__.__name__, i, err.args[i], expected_arg) + print "Caught expected exception %s(%s)" % \ + (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) + else: + assert False, "Failed to raise " + exception_class.__class__.__name__ + + # helper to raise specified exception + def raiser(exception): + raise exception + + # the usual + def assert_equals(a, b): + assert a == b, "%r != %r" % (a, b) + + # ------------------------ Sanity check the above ------------------------ + class SomeError(Exception): pass + # Without extra args, accept any err.args value + with exc(SomeError): + raiser(SomeError("abc")) + # With extra args, accept only the specified value + with exc(SomeError, "abc"): + raiser(SomeError("abc")) + with exc(AssertionError): + with exc(SomeError, "abc"): + raiser(SomeError("def")) + with exc(AssertionError): + with exc(socket.error, errno.EADDRINUSE): + raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) + + # ----------- freeport() without engaging socket functionality ----------- + # If portlist is empty, freeport() raises StopIteration. + with exc(StopIteration): + freeport([], None) + + assert_equals(freeport([17], str), ("17", 17)) + + # This is the magic exception that should prompt us to retry + inuse = socket.error(errno.EADDRINUSE, 'Address already in use') + # Get the iterator to our ports list so we can check later if we've used all + ports = iter(xrange(5)) + with exc(socket.error, errno.EADDRINUSE): + freeport(ports, lambda port: raiser(inuse)) + # did we entirely exhaust 'ports'? + with exc(StopIteration): + ports.next() + + ports = iter(xrange(2)) + # Any exception but EADDRINUSE should quit immediately + with exc(SomeError): + freeport(ports, lambda port: raiser(SomeError())) + assert_equals(ports.next(), 1) + + # ----------- freeport() with platform-dependent socket stuff ------------ + # This is what we should've had unit tests to begin with (see CHOP-661). + def newbind(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', port)) + return sock + + bound0, port0 = freeport(xrange(7777, 7780), newbind) + assert_equals(port0, 7777) + bound1, port1 = freeport(xrange(7777, 7780), newbind) + assert_equals(port1, 7778) + bound2, port2 = freeport(xrange(7777, 7780), newbind) + assert_equals(port2, 7779) + with exc(socket.error, errno.EADDRINUSE): + bound3, port3 = freeport(xrange(7777, 7780), newbind) + +if __name__ == "__main__": + test_freeport() -- cgit v1.2.3 From 6c1196d052e876bddd75bf9eeb71f75d64e60cc5 Mon Sep 17 00:00:00 2001 From: Tank_Master Date: Tue, 12 Jun 2012 09:02:47 -0700 Subject: Enable /LARGEADDRESSAWARE compiler flag for windows compiling This allows the app to allocate up to 4GB memory on 64bit hosts and 32bit hosts with PAE instead of 2GB 32bit stock OSs will still be limited to 2GB, with no bemnifit or negigitive impact This has the benifit of not crashing the viewer when the viewer memory exceeds 2GB on 64bit hosts Linux and Mac by nature use up to 4GB for a 32bit app, even on a 32bit host --- indra/cmake/00-Common.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 98eeed09b3..303f7e5608 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -51,6 +51,7 @@ if (WINDOWS) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2 -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0" CACHE STRING "C++ compiler release options" FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") set(CMAKE_CXX_STANDARD_LIBRARIES "") set(CMAKE_C_STANDARD_LIBRARIES "") -- cgit v1.2.3 From 361a413d25509e0055b5f0dbaae7a5a50264a84f Mon Sep 17 00:00:00 2001 From: Tank_Master Date: Tue, 12 Jun 2012 09:06:12 -0700 Subject: Add myself to contributions.txt --- doc/contributions.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/contributions.txt b/doc/contributions.txt index 04edfb4bea..dffb41fb78 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -1091,6 +1091,7 @@ Sudane Erato Synystyr Texan Takeda Terrawyng TankMaster Finesmith + OPEN-142 STORM-1100 STORM-1602 STORM-1258 -- cgit v1.2.3 From 24e16e1632974057013b86300bb60954ea6f5684 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 12:41:09 -0400 Subject: Convert _refcounted.h over to using LLAtomic32<>. Beware of bad documentation. operator--(int) does not return what the header claimed it did. --- indra/llcommon/llapr.h | 2 +- indra/llcorehttp/_refcounted.h | 52 ++++++++++++++-------------------------- indra/llcorehttp/bufferarray.cpp | 6 ++--- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f..034546c3f3 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -168,7 +168,7 @@ public: void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- + Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: apr_uint32_t mData; diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h index 72cef6b342..a96c65fb6b 100644 --- a/indra/llcorehttp/_refcounted.h +++ b/indra/llcorehttp/_refcounted.h @@ -28,9 +28,11 @@ #define LLCOREINT__REFCOUNTED_H_ +#include "linden_common.h" + #include -#include "linden_common.h" +#include "llapr.h" namespace LLCoreInt @@ -44,7 +46,7 @@ private: void operator=(const RefCounted &); // Not defined public: - explicit RefCounted(bool const implicit) + explicit RefCounted(bool const implicit) : mRefCount(implicit) {} @@ -52,42 +54,34 @@ public: void addRef() const; void release() const; bool isLastRef() const; - int getRefCount() const; + S32 getRefCount() const; void noRef() const; - static const int NOT_REF_COUNTED = -1; + static const S32 NOT_REF_COUNTED = -1; protected: virtual ~RefCounted(); virtual void destroySelf(); private: - mutable int mRefCount; - mutable boost::mutex mRefLock; + mutable LLAtomicS32 mRefCount; }; // end class RefCounted inline void RefCounted::addRef() const { - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount >= 0); - ++mRefCount; + S32 count(mRefCount++); + llassert_always(count >= 0); } inline void RefCounted::release() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount > 0); - count = --mRefCount; - // CRITICAL SECTION - } - + S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count > 0); + count = mRefCount--; // clean ourselves up if that was the last reference if (0 == count) @@ -99,32 +93,22 @@ inline void RefCounted::release() const inline bool RefCounted::isLastRef() const { - int count(0); - { - // CRITICAL SECTION - boost::mutex::scoped_lock lock(mRefLock); - - llassert_always(mRefCount != NOT_REF_COUNTED); - llassert_always(mRefCount >= 1); - count = mRefCount; - // CRITICAL SECTION - } - + const S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count >= 1); return (1 == count); } -inline int RefCounted::getRefCount() const +inline S32 RefCounted::getRefCount() const { - boost::mutex::scoped_lock lock(mRefLock); - const int result(mRefCount); + const S32 result(mRefCount); return result; } inline void RefCounted::noRef() const { - boost::mutex::scoped_lock lock(mRefLock); llassert_always(mRefCount <= 1); mRefCount = NOT_REF_COUNTED; } diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 6d5309a043..ae92057df0 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -175,7 +175,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) if (pos >= mLen) return 0; size_t len_limit(mLen - pos); - len = std::min(len, len_limit); + len = (std::min)(len, len_limit); if (0 == len) return 0; @@ -189,7 +189,7 @@ size_t BufferArray::read(size_t pos, void * dst, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(c_dst, &block.mData[offset], block_len); result += block_len; @@ -223,7 +223,7 @@ size_t BufferArray::write(size_t pos, const void * src, size_t len) { Block & block(*mBlocks[block_start]); size_t block_limit(block.mUsed - offset); - size_t block_len(std::min(block_limit, len)); + size_t block_len((std::min)(block_limit, len)); memcpy(&block.mData[offset], c_src, block_len); result += block_len; -- cgit v1.2.3 From 7adeb3923728ca84a309a6af141c148ce38066fc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 17:42:33 -0400 Subject: HTTP Proxy, PUT & POST, unit tests and refactoring. Implemented/modified PUT & POST to not used chunked encoding for the request. Made the unit test much happier and probably a better thing for the pipeline. Have a cheesy static & dynamic proxy capability using both local options and a way to wire into LLProxy in llmessages. Not a clean thing but it will get the proxy path working with both socks5 & http proxies. Refactoring to get rid of unneeded library handler and unified an HttpStatus return for all requests. Big batch of code removed as a result of that and more is possible as well as some syscall avoidance with a bit more work. Boosted the unit tests for simple PUT & POST test which revealed the test harness does *not* like chunked encoding so we'll avoid it for now (and don't really need it in any of our schemes). --- indra/llcorehttp/CMakeLists.txt | 5 + indra/llcorehttp/_httpopcancel.cpp | 11 -- indra/llcorehttp/_httpopcancel.h | 3 - indra/llcorehttp/_httpoperation.cpp | 18 +- indra/llcorehttp/_httpoperation.h | 13 +- indra/llcorehttp/_httpoprequest.cpp | 63 +++++-- indra/llcorehttp/_httpoprequest.h | 7 + indra/llcorehttp/_httpopsetget.cpp | 105 +++++++++++ indra/llcorehttp/_httpopsetget.h | 78 ++++++++ indra/llcorehttp/_httpopsetpriority.cpp | 14 -- indra/llcorehttp/_httpopsetpriority.h | 4 +- indra/llcorehttp/_httppolicy.cpp | 6 + indra/llcorehttp/_httppolicy.h | 4 +- indra/llcorehttp/_httppolicyglobal.cpp | 38 ++-- indra/llcorehttp/_httppolicyglobal.h | 7 +- indra/llcorehttp/_httpservice.cpp | 11 +- indra/llcorehttp/_httpservice.h | 10 +- indra/llcorehttp/httprequest.cpp | 151 ++++++++-------- indra/llcorehttp/httprequest.h | 44 ++++- indra/llcorehttp/tests/llcorehttp_test.cpp | 6 + indra/llcorehttp/tests/test_httpoperation.hpp | 5 +- indra/llcorehttp/tests/test_httprequest.hpp | 239 +++++++++++++++++++++++++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 10 +- indra/newview/llappviewer.cpp | 13 ++ 24 files changed, 702 insertions(+), 163 deletions(-) create mode 100644 indra/llcorehttp/_httpopsetget.cpp create mode 100644 indra/llcorehttp/_httpopsetget.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index a0827286e3..4273b32fe3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -10,12 +10,14 @@ include(OpenSSL) include(ZLIB) include(LLCoreHttp) include(LLAddBuildTest) +include(LLMessage) include(LLCommon) include(Tut) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) include_directories( + ${LLMESSAGE_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLCOREHTTP_INCLUDE_DIRS} ) @@ -31,6 +33,7 @@ set(llcorehttp_SOURCE_FILES _httpopcancel.cpp _httpoperation.cpp _httpoprequest.cpp + _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp _httppolicyglobal.cpp @@ -54,6 +57,7 @@ set(llcorehttp_HEADER_FILES _httpopcancel.h _httpoperation.h _httpoprequest.h + _httpopsetget.h _httpopsetpriority.h _httppolicy.h _httppolicyglobal.h @@ -113,6 +117,7 @@ if (LL_TESTS) set(test_libs ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} ${CURL_LIBRARIES} diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 69dbff4bb4..ad624d2e57 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -66,17 +66,6 @@ void HttpOpCancel::stageFromRequest(HttpService * service) } -void HttpOpCancel::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index fab6f1f362..6d1e0f8774 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -59,13 +59,10 @@ private: public: virtual void stageFromRequest(HttpService *); - - virtual void visitNotifier(HttpRequest * request); public: // Request data HttpHandle mHandle; - }; // end class HttpOpCancel diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index d966efd12b..b5c58013d4 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -47,7 +47,6 @@ namespace LLCore HttpOperation::HttpOperation() : LLCoreInt::RefCounted(true), mReplyQueue(NULL), - mLibraryHandler(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), mReqPriority(0U) @@ -57,13 +56,12 @@ HttpOperation::HttpOperation() HttpOperation::~HttpOperation() { - setHandlers(NULL, NULL, NULL); + setReplyPath(NULL, NULL); } -void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler) +void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * user_handler) { if (reply_queue != mReplyQueue) { @@ -80,9 +78,6 @@ void HttpOperation::setHandlers(HttpReplyQueue * reply_queue, mReplyQueue = reply_queue; } - // Not refcounted - mLibraryHandler = lib_handler; - // Not refcounted mUserHandler = user_handler; } @@ -121,11 +116,12 @@ void HttpOperation::stageFromActive(HttpService *) void HttpOperation::visitNotifier(HttpRequest *) { - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); - mLibraryHandler->onCompleted(static_cast(this), response); + response->setStatus(mStatus); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -142,7 +138,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mReplyQueue && mLibraryHandler) + if (mReplyQueue) { addRef(); mReplyQueue->addOp(this); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 01e26029d2..c93aa2def9 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -80,9 +80,8 @@ private: void operator=(const HttpOperation &); // Not defined public: - void setHandlers(HttpReplyQueue * reply_queue, - HttpHandler * lib_handler, - HttpHandler * user_handler); + void setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * handler); HttpHandler * getUserHandler() const { @@ -102,13 +101,15 @@ protected: protected: HttpReplyQueue * mReplyQueue; // Have refcount - HttpHandler * mLibraryHandler; // Have refcount - HttpHandler * mUserHandler; // Have refcount + HttpHandler * mUserHandler; public: + // Request Data HttpRequest::policy_t mReqPolicy; HttpRequest::priority_t mReqPriority; - + + // Reply Data + HttpStatus mStatus; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ea0b99303e..e2550d057e 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -44,6 +44,7 @@ #include "_httplibcurl.h" #include "llhttpstatuscodes.h" +#include "llproxy.h" namespace { @@ -207,7 +208,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) { static const HttpStatus partial_content(HTTP_PARTIAL_CONTENT, HE_SUCCESS); - if (mLibraryHandler) + if (mUserHandler) { HttpResponse * response = new HttpResponse(); response->setStatus(mStatus); @@ -219,7 +220,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) response->setRange(mReplyOffset, mReplyLength); } - mLibraryHandler->onCompleted(static_cast(this), response); + mUserHandler->onCompleted(static_cast(this), response); response->release(); } @@ -304,6 +305,39 @@ 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, + HttpOptions * options, + HttpHeaders * headers) +{ + HttpStatus status; + + mProcFlags = 0; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqMethod = HOR_PUT; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + mReqOptions = new HttpOptions(*options); + } + + return status; +} + + HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // Scrub transport and result data for retried op case @@ -346,8 +380,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // *FIXME: Need to deal with proxy setup... - // curl_easy_setopt(handle, CURLOPT_PROXY, ""); // *FIXME: Revisit this old DNS timeout setting - may no longer be valid curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); @@ -361,18 +393,31 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); - std::string opt_value; + const std::string * opt_value(NULL); if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); } if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value.c_str()); + if (*opt_value == "LLProxy") + { + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); + } + else + { + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } } switch (mReqMethod) @@ -394,7 +439,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast(NULL)); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); } break; @@ -409,7 +453,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Transfer-Encoding: chunked"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 6dcf30ca0c..80893beb40 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -91,6 +91,13 @@ public: HttpOptions * options, HttpHeaders * headers); + HttpStatus setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus prepareRequest(HttpService * service); virtual HttpStatus cancel(); diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp new file mode 100644 index 0000000000..21e058b2be --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -0,0 +1,105 @@ +/** + * @file _httpopsetget.cpp + * @brief Definitions for internal class HttpOpSetGet + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetget.h" + +#include +#include + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpSetget +// ================================== + + +HttpOpSetGet::HttpOpSetGet() + : HttpOperation(), + mIsGlobal(false), + mDoSet(false), + mSetting(-1), // Nothing requested + mLongValue(0L) +{} + + +HttpOpSetGet::~HttpOpSetGet() +{} + + +void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +{ + mIsGlobal = true; + mSetting = setting; +} + + +void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +{ + mIsGlobal = true; + mDoSet = true; + mSetting = setting; + mStrValue = value; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ + HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); + HttpRequest::EGlobalPolicy setting(static_cast(mSetting)); + + if (mDoSet) + { + mStatus = pol_opt.set(setting, mStrValue); + } + if (mStatus) + { + const std::string * value; + if ((mStatus = pol_opt.get(setting, value))) + { + mStrValue = *value; + } + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h new file mode 100644 index 0000000000..e065eb4c30 --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.h @@ -0,0 +1,78 @@ +/** + * @file _httpopsetget.h + * @brief Internal declarations for the HttpOpSetGet subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPSETGET_H_ +#define _LLCORE_HTTP_OPSETGET_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetGet requests dynamic changes to policy and +/// configuration settings. + +class HttpOpSetGet : public HttpOperation +{ +public: + HttpOpSetGet(); + virtual ~HttpOpSetGet(); + +private: + HttpOpSetGet(const HttpOpSetGet &); // Not defined + void operator=(const HttpOpSetGet &); // Not defined + +public: + void setupGet(HttpRequest::EGlobalPolicy setting); + void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); + + virtual void stageFromRequest(HttpService *); + +public: + // Request data + bool mIsGlobal; + bool mDoSet; + int mSetting; + long mLongValue; + std::string mStrValue; + +}; // end class HttpOpSetGet + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPSETGET_H_ + diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp index b0ee577087..d48c7a0b7d 100644 --- a/indra/llcorehttp/_httpopsetpriority.cpp +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -60,18 +60,4 @@ void HttpOpSetPriority::stageFromRequest(HttpService * service) } -void HttpOpSetPriority::visitNotifier(HttpRequest * request) -{ - if (mLibraryHandler) - { - HttpResponse * response = new HttpResponse(); - - response->setStatus(mStatus); - mLibraryHandler->onCompleted(static_cast(this), response); - - response->release(); - } -} - - } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index b972f50fff..f1e94b6e43 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -56,10 +56,8 @@ private: public: virtual void stageFromRequest(HttpService *); - virtual void visitNotifier(HttpRequest * request); - protected: - HttpStatus mStatus; + // Request Data HttpHandle mHandle; HttpRequest::priority_t mPriority; }; // end class HttpOpSetPriority diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 72bb6f14e4..8ee3f88658 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -71,6 +71,12 @@ HttpPolicy::~HttpPolicy() } +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +{ + mGlobalOptions = global; +} + + void HttpPolicy::addOp(HttpOpRequest * op) { const int policy_class(op->mReqPolicy); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 14f6a9a676..73c22bab78 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -95,7 +95,9 @@ public: { return mGlobalOptions; } - + + void setPolicies(const HttpPolicyGlobal & global); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 877b85896f..d95d73cfba 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -32,7 +32,7 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() - : mValidMask(0UL), + : mSetMask(0UL), mConnectionLimit(32L) {} @@ -41,6 +41,20 @@ HttpPolicyGlobal::~HttpPolicyGlobal() {} +HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mCAPath = other.mCAPath; + mCAFile = other.mCAFile; + mHttpProxy = other.mHttpProxy; + } + return *this; +} + + HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) { switch (opt) @@ -53,7 +67,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -78,7 +92,7 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } - mValidMask |= 1UL << int(opt); + mSetMask |= 1UL << int(opt); return HttpStatus(); } @@ -90,7 +104,7 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; value = mConnectionLimit; break; @@ -103,28 +117,28 @@ HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, std::string & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); - + switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAPath; + value = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mCAFile; + value = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mValidMask & (1UL << int(opt)))) + if (! (mSetMask & (1UL << int(opt)))) return not_set; - value = mHttpProxy; + value = &mHttpProxy; break; default: diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index 39ffbcb9bb..f4bb4d4b25 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -40,18 +40,19 @@ public: HttpPolicyGlobal(); ~HttpPolicyGlobal(); + HttpPolicyGlobal & operator=(const HttpPolicyGlobal &); + private: HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined - void operator=(const HttpPolicyGlobal &); // Not defined public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); public: - unsigned long mValidMask; + unsigned long mSetMask; long mConnectionLimit; std::string mCAPath; std::string mCAFile; diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index b038bdb720..920a3f3b6d 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -79,11 +79,8 @@ HttpService::~HttpService() mTransport = NULL; } - if (mPolicy) - { - delete mPolicy; - mPolicy = NULL; - } + delete mPolicy; + mPolicy = NULL; if (mThread) { @@ -145,6 +142,10 @@ void HttpService::startThread() { mThread->release(); } + + // Push current policy definitions + mPolicy->setPolicies(mPolicyGlobal); + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally sState = RUNNING; diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 748354a8e4..3f953ec1a7 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -30,6 +30,7 @@ #include "httpcommon.h" #include "httprequest.h" +#include "_httppolicyglobal.h" namespace LLCoreInt @@ -157,6 +158,11 @@ public: { return *mTransport; } + + HttpPolicyGlobal & getGlobalOptions() + { + return mPolicyGlobal; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -173,11 +179,11 @@ protected: // === calling-thread-only data === LLCoreInt::HttpThread * mThread; - + HttpPolicyGlobal mPolicyGlobal; + // === working-thread-only data === HttpPolicy * mPolicy; // Simple pointer, has ownership HttpLibcurl * mTransport; // Simple pointer, has ownership - }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2f36168f8b..089eee76f3 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -34,6 +34,7 @@ #include "_httpoprequest.h" #include "_httpopsetpriority.h" #include "_httpopcancel.h" +#include "_httpopsetget.h" #include "lltimer.h" @@ -48,39 +49,6 @@ bool has_inited(false); namespace LLCore { -// ==================================== -// InternalHandler Implementation -// ==================================== - - -class HttpRequest::InternalHandler : public HttpHandler -{ -public: - InternalHandler(HttpRequest & request) - : mRequest(request) - {} - -protected: - InternalHandler(const InternalHandler &); // Not defined - void operator=(const InternalHandler &); // Not defined - -public: - void onCompleted(HttpHandle handle, HttpResponse * response) - { - HttpOperation * op(static_cast(handle)); - HttpHandler * user_handler(op->getUserHandler()); - if (user_handler) - { - user_handler->onCompleted(handle, response); - } - } - -protected: - HttpRequest & mRequest; - -}; // end class HttpRequest::InternalHandler - - // ==================================== // HttpRequest Implementation // ==================================== @@ -92,15 +60,12 @@ HttpRequest::policy_t HttpRequest::sNextPolicyID(1); HttpRequest::HttpRequest() : //HttpHandler(), mReplyQueue(NULL), - mRequestQueue(NULL), - mSelfHandler(NULL) + mRequestQueue(NULL) { mRequestQueue = HttpRequestQueue::instanceOf(); mRequestQueue->addRef(); mReplyQueue = new HttpReplyQueue(); - - mSelfHandler = new InternalHandler(*this); } @@ -117,9 +82,6 @@ HttpRequest::~HttpRequest() mReplyQueue->release(); mReplyQueue = NULL; } - - delete mSelfHandler; - mSelfHandler = NULL; } @@ -132,7 +94,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -140,7 +102,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri { // *FIXME: Fail if thread is running. - return HttpService::instanceOf()->getPolicy().getGlobalOptions().set(opt, value); + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } @@ -192,7 +154,7 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -220,7 +182,7 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, mLastReqStatus = status; return handle; } - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfers refcount mLastReqStatus = status; @@ -230,19 +192,31 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, } -HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +HttpHandle HttpRequest::requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) { HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpCancel * op = new HttpOpCancel(handle); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + mLastReqStatus = status; - ret_handle = static_cast(op); + handle = static_cast(op); - return ret_handle; + return handle; } @@ -252,7 +226,7 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpNull * op = new HttpOpNull(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; @@ -262,23 +236,6 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, - HttpHandler * handler) -{ - HttpStatus status; - HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - - HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); - op->setHandlers(mReplyQueue, mSelfHandler, handler); - mRequestQueue->addOp(op); // transfer refcount as well - - mLastReqStatus = status; - ret_handle = static_cast(op); - - return ret_handle; -} - - HttpStatus HttpRequest::update(long millis) { const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); @@ -302,6 +259,38 @@ HttpStatus HttpRequest::update(long millis) // Request Management Methods // ==================================== +HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(handle); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + + +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setReplyPath(mReplyQueue, handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + ret_handle = static_cast(op); + + return ret_handle; +} + // ==================================== // Utility Methods @@ -350,7 +339,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); HttpOpStop * op = new HttpOpStop(); - op->setHandlers(mReplyQueue, mSelfHandler, user_handler); + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfer refcount as well + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + +// ==================================== +// Dynamic Policy Methods +// ==================================== + +HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetGet * op = new HttpOpSetGet(); + op->setupSet(GP_HTTP_PROXY, proxy); + op->setReplyPath(mReplyQueue, handler); mRequestQueue->addOp(op); // transfer refcount as well mLastReqStatus = status; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 01dbfba6dd..a953aa28d0 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -124,8 +124,8 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); - HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); /// Create a new policy class into which requests can be made. /// @@ -236,6 +236,32 @@ public: HttpHandler * handler); + /// + /// @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. + /// @param url + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a NoOp request. /// The request is queued and serviced by the working thread which /// immediately processes it and returns the request to the reply @@ -325,13 +351,20 @@ public: HttpHandle requestStopThread(HttpHandler * handler); /// @} + + /// @name DynamicPolicyMethods + /// + /// @{ + + /// Request that a running transport pick up a new proxy setting. + /// An empty string will indicate no proxy is to be used. + HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); + + /// @} protected: void generateNotification(HttpOperation * op); - class InternalHandler; - friend class InternalHandler; - private: /// @name InstanceData /// @@ -339,7 +372,6 @@ private: HttpStatus mLastReqStatus; HttpReplyQueue * mReplyQueue; HttpRequestQueue * mRequestQueue; - InternalHandler * mSelfHandler; /// @} diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index f59361ab53..2b36d3a982 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -44,6 +44,8 @@ #include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" +#include "llproxy.h" + unsigned long ssl_thread_id_callback(void); void ssl_locking_callback(int mode, int type, const char * file, int line); @@ -91,11 +93,15 @@ void init_curl() CRYPTO_set_locking_callback(ssl_locking_callback); CRYPTO_set_id_callback(ssl_thread_id_callback); } + + LLProxy::getInstance(); } void term_curl() { + LLProxy::cleanupClass(); + CRYPTO_set_locking_callback(NULL); for (int i(0); i < ssl_mutex_count; ++i) { diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp index 6c3df1e9e3..17b1a96878 100644 --- a/indra/llcorehttp/tests/test_httpoperation.hpp +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -97,13 +97,12 @@ namespace tut // Get some handlers TestHandler * h1 = new TestHandler(); - TestHandler * h2 = new TestHandler(); // create a new ref counted object with an implicit reference HttpOpNull * op = new HttpOpNull(); // Add the handlers - op->setHandlers(NULL, h1, h2); + op->setReplyPath(NULL, h1); // Check ref count ensure(op->getRefCount() == 1); @@ -117,8 +116,6 @@ namespace tut // release the handlers delete h1; h1 = NULL; - delete h2; - h2 = NULL; ensure(mMemTotal == GetMemTotal()); } diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 81f8fe4a85..61698f34d8 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -27,6 +27,7 @@ #define TEST_LLCORE_HTTP_REQUEST_H_ #include "httprequest.h" +#include "bufferarray.h" #include "httphandler.h" #include "httpresponse.h" #include "_httpservice.h" @@ -604,6 +605,244 @@ void HttpRequestTestObjectType::test<6>() } } +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest PUT to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest POST to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 3e200a5c19..8c3ad805b3 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -85,7 +85,15 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def do_POST(self): # Read the provided POST data. - self.answer(self.read()) + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def do_PUT(self): + # Read the provided PUT data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) def answer(self, data): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7a44415fba..e2c13e77e3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5358,6 +5358,7 @@ void CoreHttp::init() mRequest = new LLCore::HttpRequest; + // Point to our certs or SSH/https: will fail on connect status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, gDirUtilp->getCAFile()); if (! status) @@ -5366,6 +5367,18 @@ void CoreHttp::init() << status.toString() << LL_ENDL; } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, + std::string("LLProxy")); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } status = LLCore::HttpRequest::startThread(); if (! status) -- cgit v1.2.3 From 0d80aced2477500ed5912a34e2bc4b6dbf8f719a Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Tue, 12 Jun 2012 17:01:48 -0600 Subject: for SH-3073: implement a fast cache system for texture fetching --- indra/llimage/llimage.cpp | 9 +- indra/llimage/llimage.h | 2 +- indra/newview/app_settings/settings.xml | 11 ++ indra/newview/lltexturecache.cpp | 188 ++++++++++++++++++++++++++++++-- indra/newview/lltexturecache.h | 17 ++- indra/newview/lltexturefetch.cpp | 4 +- indra/newview/llviewertexture.cpp | 46 +++++++- indra/newview/llviewertexture.h | 4 + indra/newview/llviewertexturelist.cpp | 51 +++++++++ indra/newview/llviewertexturelist.h | 2 + 10 files changed, 316 insertions(+), 18 deletions(-) diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 6775b005f4..e6b838c5b2 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -292,11 +292,16 @@ LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components) ++sRawImageCount; } -LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components) +LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components, bool no_copy) : LLImageBase() { mMemType = LLMemType::MTYPE_IMAGERAW; - if(allocateDataSize(width, height, components)) + + if(no_copy) + { + setDataAndSize(data, width, height, components); + } + else if(allocateDataSize(width, height, components)) { memcpy(getData(), data, width*height*components); } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 46e6d1a901..99023351c2 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -189,7 +189,7 @@ protected: public: LLImageRaw(); LLImageRaw(U16 width, U16 height, S8 components); - LLImageRaw(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); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5fe51c97c5..1911943842 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3258,6 +3258,17 @@ Boolean Value 0 + + FastCacheFetchEnabled + + Comment + Enable texture fast cache fetching if set + Persist + 1 + Type + Boolean + Value + 1 FeatureManagerHTTPTable diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 8632890bbb..a61e2d5c86 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -50,6 +50,8 @@ const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE;//1024; const F32 TEXTURE_CACHE_PURGE_AMOUNT = .20f; // % amount to reduce the cache by when it exceeds its limit const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead to regenerate) +const S32 TEXTURE_FAST_CACHE_ENTRY_OVERHEAD = sizeof(S32) * 4; //w, h, c, level +const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = 16 * 16 * 4 + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; class LLTextureCacheWorker : public LLWorkerClass { @@ -283,9 +285,12 @@ public: LLTextureCacheRemoteWorker(LLTextureCache* cache, U32 priority, const LLUUID& id, U8* data, S32 datasize, S32 offset, S32 imagesize, // for writes + LLPointer raw, S32 discardlevel, LLTextureCache::Responder* responder) : LLTextureCacheWorker(cache, priority, id, data, datasize, offset, imagesize, responder), - mState(INIT) + mState(INIT), + mRawImage(raw), + mRawDiscardLevel(discardlevel) { } @@ -303,6 +308,8 @@ private: }; e_state mState; + LLPointer mRawImage; + S32 mRawDiscardLevel; }; @@ -559,6 +566,11 @@ bool LLTextureCacheRemoteWorker::doWrite() if(idx < 0) { idx = mCache->setHeaderCacheEntry(mID, entry, mImageSize, mDataSize); // create the new entry. + if(idx >= 0) + { + //write to the fast cache. + llassert_always(mCache->writeToFastCache(idx, mRawImage, mRawDiscardLevel)); + } } else { @@ -658,6 +670,7 @@ bool LLTextureCacheRemoteWorker::doWrite() // Nothing else to do at that point... done = true; } + mRawImage = NULL; // Clean up and exit return done; @@ -744,10 +757,14 @@ LLTextureCache::LLTextureCache(bool threaded) mWorkersMutex(NULL), mHeaderMutex(NULL), mListMutex(NULL), + mFastCacheMutex(NULL), mHeaderAPRFile(NULL), mReadOnly(TRUE), //do not allow to change the texture cache until setReadOnly() is called. mTexturesSizeTotal(0), - mDoPurge(FALSE) + mDoPurge(FALSE), + mFastCachep(NULL), + mFastCachePoolp(NULL), + mFastCachePadBuffer(NULL) { } @@ -755,6 +772,9 @@ LLTextureCache::~LLTextureCache() { clearDeleteList() ; writeUpdatedEntries() ; + delete mFastCachep; + delete mFastCachePoolp; + FREE_MEM(LLImageBase::getPrivatePool(), mFastCachePadBuffer); } ////////////////////////////////////////////////////////////////////////////// @@ -879,15 +899,15 @@ BOOL LLTextureCache::isInLocal(const LLUUID& id) ////////////////////////////////////////////////////////////////////////////// //static -const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB -F32 LLTextureCache::sHeaderCacheVersion = 1.4f; -U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE; +F32 LLTextureCache::sHeaderCacheVersion = 1.7f; +U32 LLTextureCache::sCacheMaxEntries = 1024 * 1024; //~1 million textures. S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit const char* entries_filename = "texture.entries"; const char* cache_filename = "texture.cache"; const char* old_textures_dirname = "textures"; //change the location of the texture cache to prevent from being deleted by old version viewers. const char* textures_dirname = "texturecache"; +const char* fast_cache_filename = "FastCache.cache"; void LLTextureCache::setDirNames(ELLPath location) { @@ -896,6 +916,7 @@ void LLTextureCache::setDirNames(ELLPath location) mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename); mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename); mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname); + mFastCacheFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, fast_cache_filename); } void LLTextureCache::purgeCache(ELLPath location) @@ -938,8 +959,8 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache { llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized. - S64 header_size = (max_size * 2) / 10; - S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE; + S64 header_size = (max_size / 100) * 36; //0.36 * max_size + S64 max_entries = header_size / (TEXTURE_CACHE_ENTRY_SIZE + TEXTURE_FAST_CACHE_ENTRY_SIZE); sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries)); header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE; max_size -= header_size; @@ -981,6 +1002,7 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized. + openFastCache(true); return max_size; // unused cache space } @@ -1751,7 +1773,7 @@ LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 pri LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, NULL, size, offset, - 0, responder); + 0, NULL, 0, responder); handle_t handle = worker->read(); mReaders[handle] = worker; return handle; @@ -1789,6 +1811,7 @@ bool LLTextureCache::readComplete(handle_t handle, bool abort) LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, + LLPointer rawimage, S32 discardlevel, WriteResponder* responder) { if (mReadOnly) @@ -1807,12 +1830,159 @@ LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 prio LLMutexLock lock(&mWorkersMutex); LLTextureCacheWorker* worker = new LLTextureCacheRemoteWorker(this, priority, id, data, datasize, 0, - imagesize, responder); + imagesize, rawimage, discardlevel, responder); handle_t handle = worker->write(); mWriters[handle] = worker; return handle; } +//called in the main thread +LLPointer LLTextureCache::readFromFastCache(const LLUUID& id, S32& discardlevel) +{ + U32 offset; + { + LLMutexLock lock(&mHeaderMutex); + id_map_t::const_iterator iter = mHeaderIDMap.find(id); + if(iter == mHeaderIDMap.end()) + { + return NULL; //not in the cache + } + + offset = iter->second; + } + offset *= TEXTURE_FAST_CACHE_ENTRY_SIZE; + + U8* data; + S32 head[4]; + { + LLMutexLock lock(&mFastCacheMutex); + + openFastCache(); + + mFastCachep->seek(APR_SET, offset); + + llassert_always(mFastCachep->read(head, TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) == TEXTURE_FAST_CACHE_ENTRY_OVERHEAD); + + S32 image_size = head[0] * head[1] * head[2]; + if(!image_size) //invalid + { + closeFastCache(); + return NULL; + } + discardlevel = head[3]; + + data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), image_size); + llassert_always(mFastCachep->read(data, image_size) == image_size); + closeFastCache(); + } + LLPointer raw = new LLImageRaw(data, head[0], head[1], head[2], true); + + return raw; +} + +//return the fast cache location +bool LLTextureCache::writeToFastCache(S32 id, LLPointer raw, S32 discardlevel) +{ + //rescale image if needed + S32 w, h, c; + w = raw->getWidth(); + h = raw->getHeight(); + c = raw->getComponents(); + S32 i = 0 ; + + while(((w >> i) * (h >> i) * c) > TEXTURE_FAST_CACHE_ENTRY_SIZE - TEXTURE_FAST_CACHE_ENTRY_OVERHEAD) + { + ++i ; + } + + if(i) + { + w >>= i; + h >>= i; + if(w * h *c > 0) //valid + { + LLPointer newraw = new LLImageRaw(raw->getData(), raw->getWidth(), raw->getHeight(), raw->getComponents()); + newraw->scale(w, h) ; + raw = newraw; + + discardlevel += i ; + } + } + + //copy data + memcpy(mFastCachePadBuffer, &w, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32), &h, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32) * 2, &c, sizeof(S32)); + memcpy(mFastCachePadBuffer + sizeof(S32) * 3, &discardlevel, sizeof(S32)); + if(w * h * c > 0) //valid + { + memcpy(mFastCachePadBuffer + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD, raw->getData(), w * h * c); + } + S32 offset = id * TEXTURE_FAST_CACHE_ENTRY_SIZE; + + { + LLMutexLock lock(&mFastCacheMutex); + + openFastCache(); + + mFastCachep->seek(APR_SET, offset); + llassert_always(mFastCachep->write(mFastCachePadBuffer, TEXTURE_FAST_CACHE_ENTRY_SIZE) == TEXTURE_FAST_CACHE_ENTRY_SIZE); + + closeFastCache(true); + } + + return true; +} + +void LLTextureCache::openFastCache(bool first_time) +{ + if(!mFastCachep) + { + if(first_time) + { + if(!mFastCachePadBuffer) + { + mFastCachePadBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), TEXTURE_FAST_CACHE_ENTRY_SIZE); + } + mFastCachePoolp = new LLVolatileAPRPool(); + if (LLAPRFile::isExist(mFastCacheFileName, mFastCachePoolp)) + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + else + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_CREATE|APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + } + else + { + mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ; + } + + mFastCacheTimer.reset(); + } + return; +} + +void LLTextureCache::closeFastCache(bool forced) +{ + static const F32 timeout = 10.f ; //seconds + + if(!mFastCachep) + { + return ; + } + + if(!forced && mFastCacheTimer.getElapsedTimeF32() < timeout) + { + return ; + } + + delete mFastCachep; + mFastCachep = NULL; + return; +} + bool LLTextureCache::writeComplete(handle_t handle, bool abort) { lockWorkers(); diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h index dd0cc9b4bd..e3fc957fd2 100644 --- a/indra/newview/lltexturecache.h +++ b/indra/newview/lltexturecache.h @@ -36,6 +36,7 @@ class LLImageFormatted; class LLTextureCacheWorker; +class LLImageRaw; class LLTextureCache : public LLWorkerThread { @@ -113,8 +114,9 @@ public: handle_t readFromCache(const LLUUID& id, U32 priority, S32 offset, S32 size, ReadResponder* responder); bool readComplete(handle_t handle, bool abort); - handle_t writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, + handle_t writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize, LLPointer rawimage, S32 discardlevel, WriteResponder* responder); + LLPointer readFromFastCache(const LLUUID& id, S32& discardlevel); bool writeComplete(handle_t handle, bool abort = false); void prioritizeWrite(handle_t handle); @@ -171,12 +173,18 @@ private: void lockHeaders() { mHeaderMutex.lock(); } void unlockHeaders() { mHeaderMutex.unlock(); } + void openFastCache(bool first_time = false); + void closeFastCache(bool forced = false); + bool writeToFastCache(S32 id, LLPointer raw, S32 discardlevel); + private: // Internal LLMutex mWorkersMutex; LLMutex mHeaderMutex; LLMutex mListMutex; + LLMutex mFastCacheMutex; LLAPRFile* mHeaderAPRFile; + LLVolatileAPRPool* mFastCachePoolp; typedef std::map handle_map_t; handle_map_t mReaders; @@ -193,12 +201,17 @@ private: // HEADERS (Include first mip) std::string mHeaderEntriesFileName; std::string mHeaderDataFileName; + std::string mFastCacheFileName; EntriesInfo mHeaderEntriesInfo; std::set mFreeList; // deleted entries std::set mLRU; - typedef std::map id_map_t; + typedef std::map id_map_t; id_map_t mHeaderIDMap; + LLAPRFile* mFastCachep; + LLFrameTimer mFastCacheTimer; + U8* mFastCachePadBuffer; + // BODIES (TEXTURES minus headers) std::string mTexturesDirName; typedef std::map size_map_t; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f47f3864dd..5a9cf4aeb6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1536,7 +1536,7 @@ bool LLTextureFetchWorker::doWork(S32 param) CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, - mFileSize, responder); + mFileSize, mRawImage, mDecodedDiscard, responder); // fall through } @@ -3551,7 +3551,7 @@ void LLTextureFetchDebugger::debugCacheWrite() mFetchingHistory[i].mCacheHandle = mTextureCache->writeToCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, mFetchingHistory[i].mFormattedImage->getData(), mFetchingHistory[i].mFetchedSize, mFetchingHistory[i].mDecodedLevel == 0 ? mFetchingHistory[i].mFetchedSize : mFetchingHistory[i].mFetchedSize + 1, - new LLDebuggerCacheWriteResponder(this, i)); + NULL, 0, new LLDebuggerCacheWriteResponder(this, i)); } } } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index ad7e50b788..1226c98f1c 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -62,6 +62,7 @@ #include "llmediaentry.h" #include "llvovolume.h" #include "llviewermedia.h" +#include "lltexturecache.h" /////////////////////////////////////////////////////////////////////////////// // statics @@ -1257,6 +1258,7 @@ void LLViewerFetchedTexture::init(bool firstinit) mRequestDeltaTime = 0.f; mForSculpt = FALSE ; mIsFetched = FALSE ; + mInFastCacheList = FALSE; mCachedRawImage = NULL ; mCachedRawDiscardLevel = -1 ; @@ -1313,7 +1315,38 @@ void LLViewerFetchedTexture::cleanup() mCachedRawDiscardLevel = -1 ; mCachedRawImageReady = FALSE ; mSavedRawImage = NULL ; - mSavedRawDiscardLevel = -1; +} + +//access the fast cache +void LLViewerFetchedTexture::loadFromFastCache() +{ + if(!mInFastCacheList) + { + return; //no need to access the fast cache. + } + mInFastCacheList = FALSE; + + mRawImage = LLAppViewer::getTextureCache()->readFromFastCache(getID(), mRawDiscardLevel) ; + if(mRawImage.notNull()) + { + mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; + mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + setTexelsPerImage(); + + if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) + { + //discard all oversized textures. + destroyRawImage(); + setIsMissingAsset(); + mRawDiscardLevel = INVALID_DISCARD_LEVEL ; + } + else + { + mRequestedDiscardLevel = mDesiredDiscardLevel + 1; + mIsRawImageValid = TRUE; + addToCreateTexture() ; + } + } } void LLViewerFetchedTexture::setForSculpt() @@ -1741,7 +1774,7 @@ F32 LLViewerFetchedTexture::calcDecodePriority() S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired; ddiscard = llclamp(ddiscard, 0, MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY); priority = (ddiscard + 1) * PRIORITY_DELTA_DISCARD_LEVEL_FACTOR; - setAdditionalDecodePriority(1.0f) ;//boost the textures without any data so far. + setAdditionalDecodePriority(0.1f) ;//boost the textures without any data so far. } else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel)) { @@ -1953,6 +1986,10 @@ bool LLViewerFetchedTexture::updateFetch() { return false; // process any raw image data in callbacks before replacing } + if(mInFastCacheList) + { + return false; + } S32 current_discard = getCurrentDiscardLevelForFetching() ; S32 desired_discard = getDesiredDiscardLevel(); @@ -2082,6 +2119,11 @@ bool LLViewerFetchedTexture::updateFetch() { make_request = false; } + else if(mCachedRawImage.notNull() && (current_discard < 0 || current_discard > mCachedRawDiscardLevel)) + { + make_request = false; + switchToCachedImage() ; //use the cached raw data first + } //else if (!isJustBound() && mCachedRawImageReady) //{ // make_request = false; diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index c1ebbd9ebe..c2747c980e 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -497,6 +497,9 @@ public: void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;} void forceToDeleteRequest(); + void loadFromFastCache(); + void setInFastCacheList(bool in_list) { mInFastCacheList = in_list; } + bool isInFastCacheList() { return mInFastCacheList; } protected: /*virtual*/ void switchToCachedImage(); S32 getCurrentDiscardLevelForFetching() ; @@ -516,6 +519,7 @@ private: private: BOOL mFullyLoaded; BOOL mInDebug; + BOOL mInFastCacheList; protected: std::string mLocalFileName; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 42d8cb9272..385c61f6c7 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -276,6 +276,7 @@ void LLViewerTextureList::shutdown() // Flush all of the references mLoadingStreamList.clear(); mCreateTextureList.clear(); + mFastCacheList.clear(); mUUIDMap.clear(); @@ -453,6 +454,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, LLGLenum primary_format, LLHost request_from_host) { + static LLCachedControl fast_cache_fetching_enabled(gSavedSettings, "FastCacheFetchEnabled"); + LLPointer imagep ; switch(texture_type) { @@ -490,6 +493,11 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, imagep->forceActive() ; } + if(fast_cache_fetching_enabled) + { + mFastCacheList.insert(imagep); + imagep->setInFastCacheList(true); + } return imagep ; } @@ -595,6 +603,7 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images"); static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_PRIORITIES("Prioritize"); static LLFastTimer::DeclareTimer FTM_IMAGE_CALLBACKS("Callbacks"); static LLFastTimer::DeclareTimer FTM_IMAGE_FETCH("Fetch"); +static LLFastTimer::DeclareTimer FTM_FAST_CACHE_IMAGE_FETCH("Fast Cache Fetch"); static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create"); static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats"); @@ -619,6 +628,11 @@ void LLViewerTextureList::updateImages(F32 max_time) LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory)); LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); + { + //loading from fast cache + LLFastTimer t(FTM_FAST_CACHE_IMAGE_FETCH); + max_time -= updateImagesLoadingFastCache(max_time); + } { LLFastTimer t(FTM_IMAGE_UPDATE_PRIORITIES); @@ -771,6 +785,10 @@ void LLViewerTextureList::updateImagesDecodePriorities() { continue; } + if(imagep->isInFastCacheList()) + { + continue; //wait for loading from the fast cache. + } imagep->processTextureStats(); F32 old_priority = imagep->getDecodePriority(); @@ -864,6 +882,36 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } +F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time) +{ + if (gGLManager.mIsDisabled) return 0.0f; + if(mFastCacheList.empty()) + { + return 0.f; + } + + // + // loading texture raw data from the fast cache directly. + // + + LLTimer timer; + image_list_t::iterator enditer = mFastCacheList.begin(); + for (image_list_t::iterator iter = mFastCacheList.begin(); + iter != mFastCacheList.end();) + { + image_list_t::iterator curiter = iter++; + enditer = iter; + LLViewerFetchedTexture *imagep = *curiter; + imagep->loadFromFastCache(); + if (timer.getElapsedTimeF32() > max_time) + { + break; + } + } + mFastCacheList.erase(mFastCacheList.begin(), enditer); + return timer.getElapsedTimeF32(); +} + void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) { if(!imagep) @@ -985,6 +1033,9 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) { LLTimer timer; + //loading from fast cache + updateImagesLoadingFastCache(max_time); + // Update texture stats and priorities std::vector > image_list; for (image_priority_list_t::iterator iter = mImageList.begin(); diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index b8f74a4ec6..3dda973d3f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -121,6 +121,7 @@ private: F32 updateImagesCreateTextures(F32 max_time); F32 updateImagesFetchTextures(F32 max_time); void updateImagesUpdateStats(); + F32 updateImagesLoadingFastCache(F32 max_time); void addImage(LLViewerFetchedTexture *image); void deleteImage(LLViewerFetchedTexture *image); @@ -174,6 +175,7 @@ public: image_list_t mLoadingStreamList; image_list_t mCreateTextureList; image_list_t mCallbackList; + image_list_t mFastCacheList; // Note: just raw pointers because they are never referenced, just compared against std::set mDirtyTextureList; -- cgit v1.2.3 From c71e808a36adef2ced44caa03a668296a2e962ca Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 12 Jun 2012 19:46:19 -0400 Subject: Really need to figure out the 'static const' problem on Windows. For now, workaround... --- indra/llcorehttp/_refcounted.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp index 2c132e1b36..11d75fdf97 100644 --- a/indra/llcorehttp/_refcounted.cpp +++ b/indra/llcorehttp/_refcounted.cpp @@ -30,6 +30,12 @@ namespace LLCoreInt { +#if ! defined(WIN32) + +const S32 RefCounted::NOT_REF_COUNTED; + +#endif // ! defined(WIN32) + RefCounted::~RefCounted() {} -- cgit v1.2.3 From 682ae001cf3a618ff6f96469e3c1f074fbb76a4e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 13 Jun 2012 11:46:38 -0400 Subject: Restore original priority scheme of LOW/HIGH. The NORMAL range doesn't do any sleeping at all and so we'll spin the core harder than we already are. Bring all idlers into the same range. --- indra/newview/lltexturefetch.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 571f9ab3b5..f5e7540e85 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -203,7 +203,7 @@ // Priority Scheme // // [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state -// [PRIORITY_NORMAL, PRIORITY_HIGH) - waiting for external event +// and other wait states // [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, // rapidly transitioning through states, // no waiting allowed @@ -1102,7 +1102,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk std::string filename = mUrl.substr(7, std::string::npos); @@ -1112,7 +1112,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else if (mUrl.empty()) { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, @@ -1241,7 +1241,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); recordTextureStart(false); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1252,7 +1252,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); //recordTextureStart(false); - //setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } @@ -1281,7 +1281,7 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mFetcher->addToNetworkQueue(this); // failsafe - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); recordTextureStart(false); } return false; @@ -1371,7 +1371,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpActive = true; mFetcher->addToHTTPQueue(mID); recordTextureStart(true); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mState = WAIT_HTTP_REQ; // fall through @@ -1495,7 +1495,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } } @@ -1504,7 +1504,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { static LLCachedControl textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it if (textures_decode_disabled) { // for debug use, don't decode @@ -1540,7 +1540,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); S32 discard = mHaveAllData ? 0 : mLoadedDiscard; - U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; + U32 image_priority = LLWorkerThread::PRIORITY_LOW | mWorkPriority; mDecoded = FALSE; mState = DECODE_IMAGE_UPDATE; LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard @@ -1612,7 +1612,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } llassert_always(datasize); - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); // Set priority first since Responder may change it + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; @@ -1654,7 +1654,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } else { - setPriority(LLWorkerThread::PRIORITY_NORMAL | mWorkPriority); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } } -- cgit v1.2.3 From 52411f83a1e92d8449f68a6528f5b78e7c391553 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 14 Jun 2012 12:01:21 -0600 Subject: fix for MAINT-758: [PUBLIC]texture discard level is inconsistent between sessions --- indra/newview/llface.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 75cd209e1a..f4e46ae3ea 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -2030,8 +2030,17 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) LLVector4a t; t.load3(camera->getOrigin().mV); lookAt.setSub(center, t); + F32 dist = lookAt.getLength3().getF32(); - dist = llmax(dist-size.getLength3().getF32(), 0.f); + dist = llmax(dist-size.getLength3().getF32(), 0.001f); + //ramp down distance for nearby objects + if (dist < 16.f) + { + dist /= 16.f; + dist *= dist; + dist *= 16.f; + } + lookAt.normalize3fast() ; //get area of circle around node -- cgit v1.2.3 From b08125a5874a89ce5210f8fb2c961ae17fb80fde Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 14 Jun 2012 16:31:48 -0400 Subject: LLMutex recursive lock, global & per-request tracing, simple GET request, LLProxy support, HttpOptions starting to work, HTTP resource waiting fixed. Non-LLThread-based threads need to do some registration or LLMutex locks taken out in these threads will not work as expected (SH-3154). We'll get a better solution later, this fixes some things for now. Tracing of operations now supported. Global and per-request (via HttpOptions) tracing levels of [0..3]. The 2 and 3 levels use libcurl's VERBOSE mode combined with CURLOPT_DEBUGFUNCTION to stream high levels of detail into the log. *Very* laggy but useful. Simple GET request supported (no Range: header). Really just a degenrate case of a ranged get but supplied an API anyway. Global option to use the LLProxy interface to setup CURL handles for either socks5 or http proxy usage. This isn't really the most encapsulated way to do this but a better solution will have to come later. The wantHeaders and tracing options are now supported in HttpOptions giving per-request controls. Big refactoring of the HTTP resource waiter in lltexturefetch. What I was doing before wasn't correct. Instead, I'm implementing the resource wait after the Semaphore model (though not using system semaphores). So instead of having a sequence like: SEND_HTTP_REQ -> WAIT_HTTP_RESOURCE -> SEND_HTTP_REQ, we now do WAIT_HTTP_RESOURCE -> WAIT_HTTP_RESOURCE2 (actual wait) -> SEND_HTTP_REQ. Works well but the prioritized filling of the corehttp library needs some performance work later. --- indra/llcommon/llthread.cpp | 7 + indra/llcommon/llthread.h | 5 + indra/llcorehttp/_httplibcurl.cpp | 24 ++- indra/llcorehttp/_httpoperation.cpp | 15 +- indra/llcorehttp/_httpoperation.h | 4 + indra/llcorehttp/_httpoprequest.cpp | 271 +++++++++++++++++++++------- indra/llcorehttp/_httpoprequest.h | 16 +- indra/llcorehttp/_httpopsetget.cpp | 4 +- indra/llcorehttp/_httppolicy.cpp | 21 ++- indra/llcorehttp/_httppolicy.h | 5 +- indra/llcorehttp/_httppolicyglobal.cpp | 52 ++++-- indra/llcorehttp/_httppolicyglobal.h | 6 +- indra/llcorehttp/_httpservice.cpp | 18 +- indra/llcorehttp/httpoptions.cpp | 18 +- indra/llcorehttp/httpoptions.h | 14 +- indra/llcorehttp/httprequest.cpp | 27 +++ indra/llcorehttp/httprequest.h | 75 +++++++- indra/llcorehttp/tests/test_httprequest.hpp | 228 ++++++++++++++++++++++- indra/newview/llappviewer.cpp | 20 +- indra/newview/lltexturefetch.cpp | 120 +++++++----- indra/newview/lltexturefetch.h | 9 + indra/newview/lltextureview.cpp | 5 +- 22 files changed, 803 insertions(+), 161 deletions(-) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index a6ad6b125c..b27b64b26f 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread() } } +void LLThread::registerThreadID() +{ +#if !LL_DARWIN + sThreadID = ++sIDIter; +#endif +} + // // Handed to the APR thread creation function // diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b52e70ab2e..54af41ec59 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -88,6 +88,11 @@ public: U32 getID() const { return mID; } + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: BOOL mPaused; diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index e134a28401..a176dd5b2a 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -159,6 +159,17 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; + if (op->mTracing > 0) + { + HttpPolicy & policy(mService->getPolicy()); + + LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " + << static_cast(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; + } + // On success, make operation active mActiveOps.insert(op); } @@ -190,10 +201,9 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode // Deactivate request op->mCurlActive = false; - // Set final status of request + // Set final status of request if it hasn't failed by other mechanisms yet if (op->mStatus) { - // Only set if it hasn't failed by other mechanisms yet op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); } if (op->mStatus) @@ -209,6 +219,16 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode curl_easy_cleanup(handle); op->mCurlHandle = NULL; + // Tracing + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Dispatch to next stage HttpPolicy & policy(mService->getPolicy()); bool still_active(policy.stageAfterCompletion(op)); diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index b5c58013d4..5a31bf90e7 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -34,6 +34,8 @@ #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "lltimer.h" + namespace LLCore { @@ -49,8 +51,10 @@ HttpOperation::HttpOperation() mReplyQueue(NULL), mUserHandler(NULL), mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), - mReqPriority(0U) + mReqPriority(0U), + mTracing(0) { + mMetricCreated = totalTime(); } @@ -113,7 +117,7 @@ void HttpOperation::stageFromActive(HttpService *) llassert_always(false); } - + void HttpOperation::visitNotifier(HttpRequest *) { if (mUserHandler) @@ -138,6 +142,13 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { + if (mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " + << static_cast(this) + << LL_ENDL; + } + if (mReplyQueue) { addRef(); diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index c93aa2def9..de4939a0ac 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -110,6 +110,10 @@ public: // Reply Data HttpStatus mStatus; + + // Tracing, debug and metrics + HttpTime mMetricCreated; + int mTracing; }; // end class HttpOperation diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index e2550d057e..f78971d8f2 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -63,6 +63,16 @@ int parse_content_range_header(char * buffer, unsigned int * last, unsigned int * length); + +// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and +// escape and format it for a tracing line in logging. Absolutely +// anything including NULs can be in the data. If @scrub is true, +// non-printing or non-ascii characters are replaced with spaces +// otherwise a %XX form of escaping is used. +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, + std::string & safe_line); + + #if defined(WIN32) // Not available on windows where the legacy strtok interface @@ -78,11 +88,6 @@ namespace LLCore { -// ================================== -// HttpOpRequest -// ================================== - - HttpOpRequest::HttpOpRequest() : HttpOperation(), mProcFlags(0U), @@ -237,6 +242,19 @@ HttpStatus HttpOpRequest::cancel() } +HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + + return HttpStatus(); +} + + HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -245,30 +263,16 @@ HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, NULL, options, headers); mReqMethod = HOR_GET; - mReqURL = url; mReqOffset = offset; mReqLength = len; if (offset || len) { mProcFlags |= PF_SCAN_RANGE_HEADER; } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -279,29 +283,10 @@ HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; - - mProcFlags = 0; - mReqPolicy = policy_id; - mReqPriority = priority; + setupCommon(policy_id, priority, url, body, options, headers); mReqMethod = HOR_POST; - mReqURL = url; - if (body) - { - body->addRef(); - mReqBody = body; - } - if (headers && ! mReqHeaders) - { - headers->addRef(); - mReqHeaders = headers; - } - if (options && ! mReqOptions) - { - mReqOptions = new HttpOptions(*options); - } - return status; + return HttpStatus(); } @@ -312,12 +297,23 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - HttpStatus status; + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PUT; + + return HttpStatus(); +} + +void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ mProcFlags = 0; mReqPolicy = policy_id; mReqPriority = priority; - mReqMethod = HOR_PUT; mReqURL = url; if (body) { @@ -331,10 +327,14 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, } if (options && ! mReqOptions) { - mReqOptions = new HttpOptions(*options); + options->addRef(); + mReqOptions = options; + if (options->getWantHeaders()) + { + mProcFlags |= PF_SAVE_HEADERS; + } + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); } - - return status; } @@ -394,30 +394,29 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); const std::string * opt_value(NULL); - if (policy.get(HttpRequest::GP_CA_PATH, opt_value)) + long opt_long(0L); + policy.get(HttpRequest::GP_LLPROXY, &opt_long); + if (opt_long) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); } - if (policy.get(HttpRequest::GP_CA_FILE, opt_value)) + else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) { - curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); + // *TODO: This is fine for now but get fuller socks/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } - if (policy.get(HttpRequest::GP_HTTP_PROXY, opt_value)) + if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) { - if (*opt_value == "LLProxy") - { - // Use the viewer-based thread-safe API which has a - // fast/safe check for proxy enable. Would like to - // encapsulate this someway... - LLProxy::getInstance()->applyProxySettings(mCurlHandle); - } - else - { - // *TODO: This is fine for now but get fuller socks/ - // authentication thing going later.... - curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); - curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); } switch (mReqMethod) @@ -463,6 +462,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) // *FIXME: fail out here break; } + + // Tracing + if (mTracing > 1) + { + curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); + } // There's a CURLOPT for this now... if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) @@ -621,6 +628,101 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi return hdr_size; } + +int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + // *FIXME: check the pointer + + std::string safe_line; + std::string tag; + bool logit(false); + len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases + + switch (info) + { + case CURLINFO_TEXT: + if (op->mTracing > 1) + { + tag = "TEXT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_IN: + if (op->mTracing > 1) + { + tag = "HEADERIN"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_OUT: + if (op->mTracing > 1) + { + tag = "HEADEROUT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_DATA_IN: + if (op->mTracing > 1) + { + tag = "DATAIN"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + case CURLINFO_DATA_OUT: + if (op->mTracing > 1) + { + tag = "DATAOUT"; + logit = true; + if (op->mTracing > 2) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + default: + logit = false; + break; + } + + if (logit) + { + LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " + << static_cast(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; + } + + return 0; +} + + } // end namespace LLCore @@ -694,6 +796,43 @@ char *strtok_r(char *str, const char *delim, char ** savestate) #endif + +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) +{ + std::string out; + len = (std::min)(len, size_t(200)); + out.reserve(3 * len); + for (int i(0); i < len; ++i) + { + unsigned char uc(static_cast(buffer[i])); + + if (uc < 32 || uc > 126) + { + if (scrub) + { + out.append(1, ' '); + } + else + { + static const char hex[] = "0123456789ABCDEF"; + char convert[4]; + + convert[0] = '%'; + convert[1] = hex[(uc >> 4) % 16]; + convert[2] = hex[uc % 16]; + convert[3] = '\0'; + out.append(convert); + } + } + else + { + out.append(1, buffer[i]); + } + } + safe_line.swap(out); +} + + } // end anonymous namespace diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 80893beb40..fc2301057c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -76,6 +76,12 @@ public: public: // Setup Methods + HttpStatus setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers); + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, HttpRequest::priority_t priority, const std::string & url, @@ -103,15 +109,23 @@ public: virtual HttpStatus cancel(); protected: + void setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; - + public: // Request data EMethod mReqMethod; diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp index 21e058b2be..c1357f9ae5 100644 --- a/indra/llcorehttp/_httpopsetget.cpp +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -89,8 +89,8 @@ void HttpOpSetGet::stageFromRequest(HttpService * service) } if (mStatus) { - const std::string * value; - if ((mStatus = pol_opt.get(setting, value))) + const std::string * value(NULL); + if ((mStatus = pol_opt.get(setting, &value))) { mStrValue = *value; } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 8ee3f88658..0e08d88276 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -107,6 +107,12 @@ void HttpPolicy::retryOp(HttpOpRequest * op) LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries << " being scheduled for " << delta << " uSecs from now." << LL_ENDL; + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } mState[policy_class].mRetryQueue.push(op); } @@ -224,8 +230,8 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } else if (op->mPolicyRetries) { - LL_WARNS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; + LL_DEBUGS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." + << LL_ENDL; } op->stageFromActive(mService); @@ -234,4 +240,15 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) } +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) +{ + if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + { + return (mState[policy_class].mReadyQueue.size() + + mState[policy_class].mRetryQueue.size()); + } + return 0; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 73c22bab78..4114f64848 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -97,7 +97,10 @@ public: } void setPolicies(const HttpPolicyGlobal & global); - + + // Get ready counts for a particular class + int getReadyCount(HttpRequest::policy_t policy_class); + protected: struct State { diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index d95d73cfba..6b1de38fd6 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -33,7 +33,9 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L) + mConnectionLimit(32L), + mTrace(0), + mUseLLProxy(0) {} @@ -50,6 +52,8 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) mCAPath = other.mCAPath; mCAFile = other.mCAFile; mHttpProxy = other.mHttpProxy; + mTrace = other.mTrace; + mUseLLProxy = other.mUseLLProxy; } return *this; } @@ -63,6 +67,14 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) mConnectionLimit = value; break; + case HttpRequest::GP_TRACE: + mTrace = llclamp(value, 0L, 3L); + break; + + case HttpRequest::GP_LLPROXY: + mUseLLProxy = llclamp(value, 0L, 1L); + break; + default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } @@ -97,54 +109,64 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long & value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = mConnectionLimit; + src = &mConnectionLimit; + break; + + case HttpRequest::GP_TRACE: + src = &mTrace; + break; + + case HttpRequest::GP_LLPROXY: + src = &mUseLLProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; return HttpStatus(); } -HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string *& value) +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) { static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + const std::string * src(NULL); switch (opt) { case HttpRequest::GP_CA_PATH: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAPath; + src = &mCAPath; break; case HttpRequest::GP_CA_FILE: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mCAFile; + src = &mCAFile; break; case HttpRequest::GP_HTTP_PROXY: - if (! (mSetMask & (1UL << int(opt)))) - return not_set; - value = &mHttpProxy; + src = &mHttpProxy; break; default: return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); } + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = src; return HttpStatus(); } diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h index f4bb4d4b25..a50d0e4188 100644 --- a/indra/llcorehttp/_httppolicyglobal.h +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -48,8 +48,8 @@ private: public: HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, long & value); - HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string *& value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); public: unsigned long mSetMask; @@ -57,6 +57,8 @@ public: std::string mCAPath; std::string mCAFile; std::string mHttpProxy; + long mTrace; + long mUseLLProxy; }; // end class HttpPolicyGlobal } // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 920a3f3b6d..beba8f08f4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -36,6 +36,7 @@ #include "_thread.h" #include "lltimer.h" +#include "llthread.h" // Tuning parameters @@ -186,8 +187,10 @@ void HttpService::shutdown() void HttpService::threadRun(LLCoreInt::HttpThread * thread) { boost::this_thread::disable_interruption di; - ELoopSpeed loop(REQUEST_SLEEP); + + LLThread::registerThreadID(); + ELoopSpeed loop(REQUEST_SLEEP); while (! mExitRequested) { loop = processRequestQueue(loop); @@ -226,6 +229,19 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) // Process operation if (! mExitRequested) { + // Setup for subsequent tracing + long tracing(0); + mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); + op->mTracing = (std::max)(op->mTracing, int(tracing)); + + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " + << static_cast(op) + << LL_ENDL; + } + + // Stage op->stageFromRequest(this); } diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 15b505f5bf..155fbda7f1 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -33,13 +33,15 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), - mWantHeaders(false) + mWantHeaders(false), + mTracing(0) {} HttpOptions::HttpOptions(const HttpOptions & rhs) : RefCounted(true), - mWantHeaders(rhs.mWantHeaders) + mWantHeaders(rhs.mWantHeaders), + mTracing(rhs.mTracing) {} @@ -47,4 +49,16 @@ HttpOptions::~HttpOptions() {} +void HttpOptions::setWantHeaders() +{ + mWantHeaders = true; +} + + +void HttpOptions::setTrace(long level) +{ + mTracing = int(level); +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 267a982dd5..0b9dfdc1de 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -67,11 +67,21 @@ protected: void operator=(const HttpOptions &); // Not defined public: + void setWantHeaders(); + bool getWantHeaders() const + { + return mWantHeaders; + } + + void setTrace(long level); + int getTrace() const + { + return mTracing; + } protected: - // *TODO: add some options bool mWantHeaders; - + long int mTracing; }; // end class HttpOptions diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 089eee76f3..2036ecfd1c 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -135,6 +135,33 @@ HttpStatus HttpRequest::getStatus() const } +HttpHandle HttpRequest::requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGet(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + mRequestQueue->addOp(op); // transfers refcount + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + + HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, priority_t priority, const std::string & url, diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index a953aa28d0..4e78ed3719 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -111,10 +111,43 @@ public: /// Maximum number of connections the library will use to /// perform operations. This is somewhat soft as the underlying /// transport will cache some connections (up to 5). - GP_CONNECTION_LIMIT, ///< Takes long giving number of connections - GP_CA_PATH, ///< System path/directory where SSL certs are stored. - GP_CA_FILE, ///< System path/file containing certs. - GP_HTTP_PROXY ///< String giving host/port to use for HTTP proxy + + /// A long value setting the maximum number of connections + /// allowed over all policy classes. Note that this will be + /// a somewhat soft value. There may be an additional five + /// connections per policy class depending upon runtime + /// behavior. + GP_CONNECTION_LIMIT, + + /// String containing a system-appropriate directory name + /// where SSL certs are stored. + GP_CA_PATH, + + /// String giving a full path to a file containing SSL certs. + GP_CA_FILE, + + /// String of host/port to use as simple HTTP proxy. This is + /// going to change in the future into something more elaborate + /// that may support richer schemes. + GP_HTTP_PROXY, + + /// Long value that if non-zero enables the use of the + /// traditional LLProxy code for http/socks5 support. If + /// enabled, has priority over GP_HTTP_PROXY. + GP_LLPROXY, + + /// Long value setting the logging trace level for the + /// library. Possible values are: + /// 0 - No tracing (default) + /// 1 - Basic tracing of request start, stop and major events. + /// 2 - Connection, header and payload size information from + /// HTTP transactions. + /// 3 - Partial logging of payload itself. + /// + /// These values are also used in the trace modes for + /// individual requests in HttpOptions. Also be aware that + /// tracing tends to impact performance of the viewer. + GP_TRACE }; /// Set a parameter on a global policy option. Calls @@ -133,7 +166,7 @@ public: /// the class in other methods. If -1, an error /// occurred and @see getStatus() may provide more /// detail on the reason. - policy_t createPolicyClass(); + static policy_t createPolicyClass(); enum EClassPolicy { @@ -157,7 +190,7 @@ public: /// @param opt Enum of option to be set. /// @param value Desired value of option. /// @return Standard status code. - HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); + static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); /// @} @@ -176,6 +209,36 @@ public: /// HttpStatus getStatus() const; + /// Queue a full HTTP GET request to be issued for entire entity. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// @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 + /// @param options (optional) + /// @param headers (optional) + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + /// Queue a full HTTP GET request to be issued with a 'Range' header. /// The request is queued and serviced by the working thread and /// notification of completion delivered to the optional HttpHandler diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 61698f34d8..5b04796c8a 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -395,7 +395,7 @@ void HttpRequestTestObjectType::test<5>() { ScopedCurlInit ready; - set_test_name("HttpRequest GET + Stop execution"); + set_test_name("HttpRequest GET to dead port + Stop execution"); // Handler can be stack-allocated *if* there are no dangling // references to it after completion of this method. @@ -496,6 +496,7 @@ void HttpRequestTestObjectType::test<5>() } } + template <> template <> void HttpRequestTestObjectType::test<6>() { @@ -517,6 +518,114 @@ void HttpRequestTestObjectType::test<6>() HttpRequest * req = NULL; + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Range: header to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + try { // Get singletons created @@ -605,8 +714,9 @@ void HttpRequestTestObjectType::test<6>() } } + template <> template <> -void HttpRequestTestObjectType::test<7>() +void HttpRequestTestObjectType::test<8>() { ScopedCurlInit ready; @@ -725,7 +835,7 @@ void HttpRequestTestObjectType::test<7>() } template <> template <> -void HttpRequestTestObjectType::test<8>() +void HttpRequestTestObjectType::test<9>() { ScopedCurlInit ready; @@ -843,6 +953,118 @@ void HttpRequestTestObjectType::test<8>() } } +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with some tracing"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + } // end namespace tut namespace diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e2c13e77e3..430dd89c3e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5356,11 +5356,9 @@ void CoreHttp::init() << LL_ENDL; } - mRequest = new LLCore::HttpRequest; - // Point to our certs or SSH/https: will fail on connect - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, - gDirUtilp->getCAFile()); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); if (! status) { LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " @@ -5371,15 +5369,22 @@ void CoreHttp::init() // Establish HTTP Proxy. "LLProxy" is a special string which directs // the code to use LLProxy::applyProxySettings() to establish any // HTTP or SOCKS proxy for http operations. - status = mRequest->setPolicyGlobalOption(LLCore::HttpRequest::GP_HTTP_PROXY, - std::string("LLProxy")); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); if (! status) { LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " << status.toString() << LL_ENDL; } - + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 0); + + // Kick the thread status = LLCore::HttpRequest::startThread(); if (! status) { @@ -5388,6 +5393,7 @@ void CoreHttp::init() << LL_ENDL; } + mRequest = new LLCore::HttpRequest; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f5e7540e85..664af02f78 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -431,6 +431,22 @@ private: void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Locks: Mw + void acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + mHttpHasResource = true; + --mFetcher->mHttpSemaphore; + } + + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + ++mFetcher->mHttpSemaphore; + } + private: enum e_state // mState { @@ -444,8 +460,9 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, @@ -532,7 +549,7 @@ private: bool mHttpActive; // Active request to http library unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; - bool mHttpReleased; // Has been released from resource wait once + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore }; ////////////////////////////////////////////////////////////////////////////// @@ -768,8 +785,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", - "SEND_HTTP_REQ", "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", @@ -836,7 +854,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), - mHttpReleased(true) + mHttpHasResource(false) { mCanUseNET = mUrl.empty() ; @@ -860,6 +878,10 @@ LLTextureFetchWorker::~LLTextureFetchWorker() llassert_always(!haveWork()); lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + releaseHttpSemaphore(); + } if (mHttpActive) { // Issue a cancel on a live request... @@ -1126,7 +1148,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -1223,7 +1245,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1287,31 +1309,38 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } - if (mState == SEND_HTTP_REQ) + if (mState == WAIT_HTTP_RESOURCE) { - if (! mCanUseHTTP) - { - return true; // abort - } - // NOTE: // control the number of the http requests issued for: // 1, not openning too many file descriptors at the same time; // 2, control the traffic of http so udp gets bandwidth. // - if (! mHttpReleased) + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->mHttpSemaphore <= 0 || mFetcher->getHttpWaitersCount()) { - // If this request hasn't been released before and it looks like - // we're busy, put this request into resource wait and allow something - // else to come to the front. - if (mFetcher->getNumHTTPRequests() >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER || - mFetcher->getHttpWaitersCount()) - { - mState = WAIT_HTTP_RESOURCE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mFetcher->addHttpWaiter(this->mID); - return false; - } + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + return false; + } + mState = SEND_HTTP_REQ; + acquireHttpSemaphore(); + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... + return false; + } + + if (mState == SEND_HTTP_REQ) + { + if (! mCanUseHTTP) + { + releaseHttpSemaphore(); + return true; // abort } mFetcher->removeFromNetworkQueue(this, false); @@ -1327,10 +1356,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } else { + releaseHttpSemaphore(); return true; // abort. } } @@ -1365,6 +1396,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { llwarns << "HTTP GET request failed for " << mID << llendl; resetFormattedData(); + releaseHttpSemaphore(); return true; // failed } @@ -1377,13 +1409,6 @@ bool LLTextureFetchWorker::doWork(S32 param) // fall through } - if (mState == WAIT_HTTP_RESOURCE) - { - // Nothing to do until releaseHttpWaiters() puts us back - // into the flow... - return false; - } - if (mState == WAIT_HTTP_REQ) { if (mLoaded) @@ -1401,6 +1426,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = INIT; mCanUseHTTP = false; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } } @@ -1423,12 +1449,14 @@ bool LLTextureFetchWorker::doWork(S32 param) // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); mState = DECODE_IMAGE; + releaseHttpSemaphore(); return false; } // Fail harder resetFormattedData(); mState = DONE; + releaseHttpSemaphore(); return true; // failed } @@ -1443,6 +1471,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // abort. mState = DONE; + releaseHttpSemaphore(); return true; } @@ -1491,6 +1520,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else @@ -2137,7 +2167,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mQAMode(qa_mode), mHttpRequest(NULL), mHttpOptions(NULL), - mHttpHeaders(NULL) + mHttpHeaders(NULL), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) { mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -3155,12 +3186,11 @@ void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) // Locks: -Mw (must not hold any worker when called) void LLTextureFetch::releaseHttpWaiters() { - if (HTTP_REQUESTS_IN_QUEUE_LOW_WATER < getNumHTTPRequests()) + if (mHttpSemaphore < HTTP_REQUESTS_IN_QUEUE_LOW_WATER) return; // Quickly make a copy of all the LLUIDs. Get off the // mutex as early as possible. - typedef std::vector uuid_vec_t; uuid_vec_t tids; @@ -3171,13 +3201,12 @@ void LLTextureFetch::releaseHttpWaiters() return; const size_t limit(mHttpWaitResource.size()); - tids.resize(limit); - wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); - for (int i(0); - i < limit && mHttpWaitResource.end() != iter; - ++i, ++iter) + tids.reserve(limit); + for (wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) { - tids[i] = *iter; + tids.push_back(*iter); } } // -Mfnq @@ -3196,28 +3225,29 @@ void LLTextureFetch::releaseHttpWaiters() tids2.insert(worker); } } + tids.clear(); // Release workers up to the high water mark. Since we aren't // holding any locks at this point, we can be in competition // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - tids.clear(); for (worker_set_t::iterator iter2(tids2.begin()); - tids2.end() != iter2 && 0 < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - getNumHTTPRequests()); + tids2.end() != iter2 && mHttpSemaphore > 0; ++iter2) { LLTextureFetchWorker * worker(* iter2); worker->lockWorkMutex(); // +Mw - if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE != worker->mState) + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) { worker->unlockWorkMutex(); // -Mw continue; } - worker->mHttpReleased = true; + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->acquireHttpSemaphore(); worker->unlockWorkMutex(); // -Mw removeHttpWaiter(worker->mID); @@ -3456,7 +3486,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) } // In QA mode, Metrics submode, log the result for ease of testing - if (fetcher->isQAMode() || true) + if (fetcher->isQAMode()) { LL_INFOS("Textures") << merged_llsd << LL_ENDL; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 4ee13d171e..50e3181623 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -312,6 +312,15 @@ private: LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + int mHttpSemaphore; // Ttf + typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 5f1d7829ed..bb1535d23d 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -234,15 +234,16 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ - { "HTW", LLColor4::green }, // WAIT_HTTP_RES { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE { "DEC", LLColor4::green }, // DECODE_IMAGE_UPDATE { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 13 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 -- cgit v1.2.3 From 888e2587e57bf296c0b9ee227a4baa3efb3f4892 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 15 Jun 2012 13:05:24 -0400 Subject: Ported example (freestanding) program to drive API & generate performance numbers. This is a command-line utility to pull content down from a service through the llcorehttp library to produce timings and resource footprints. --- indra/llcorehttp/CMakeLists.txt | 28 ++ indra/llcorehttp/examples/http_texture_load.cpp | 494 ++++++++++++++++++++++++ 2 files changed, 522 insertions(+) create mode 100644 indra/llcorehttp/examples/http_texture_load.cpp diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 4273b32fe3..9d8bae973e 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -134,5 +134,33 @@ if (LL_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" ) + # + # Example Programs + # + SET(llcorehttp_EXAMPLE_SOURCE_FILES + examples/http_texture_load.cpp + ) + + set(example_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + + add_executable(http_texture_load + ${llcorehttp_EXAMPLE_SOURCE_FILES} + ) + set_target_properties(http_texture_load + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") + + target_link_libraries(http_texture_load ${example_libs}) + endif (LL_TESTS) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp new file mode 100644 index 0000000000..83139912c9 --- /dev/null +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -0,0 +1,494 @@ +/** + * @file http_texture_load.cpp + * @brief Texture download example for core-http library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include +#include +#include +#include +#include +#if !defined(WIN32) +#include +#endif + +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "_mutex.h" + +#include +#include + + +void init_curl(); +void term_curl(); +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); +void usage(std::ostream & out); + +// Default command line settings +static int concurrency_limit(40); +static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture"; + +#if defined(WIN32) + +#define strncpy(_a, _b, _c) strncpy_s(_a, _b, _c) +#define strtok_r(_a, _b, _c) strtok_s(_a, _b, _c) + +int getopt(int argc, char * const argv[], const char *optstring); +char *optarg(NULL); +int optind(1); + +#endif + +class WorkingSet : public LLCore::HttpHandler +{ +public: + WorkingSet(); + + bool reload(LLCore::HttpRequest *); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + void loadTextureUuids(FILE * in); + +public: + struct Spec + { + std::string mUuid; + int mOffset; + int mLength; + }; + typedef std::set handle_set_t; + typedef std::vector texture_list_t; + +public: + bool mVerbose; + bool mRandomRange; + int mMaxConcurrency; + handle_set_t mHandles; + int mRemaining; + int mLimit; + int mAt; + std::string mUrl; + texture_list_t mTextures; + int mErrorsApi; + int mErrorsHttp; + int mSuccesses; + long mByteCount; +}; + + +// +// +// +int main(int argc, char** argv) +{ + bool do_random(false); + bool do_verbose(false); + + int option(-1); + while (-1 != (option = getopt(argc, argv, "u:c:h?Rv"))) + { + switch (option) + { + case 'u': + strncpy(url_format, optarg, sizeof(url_format)); + url_format[sizeof(url_format) - 1] = '\0'; + break; + + case 'c': + { + unsigned long value; + char * end; + + value = strtoul(optarg, &end, 10); + if (value < 1 || value > 100 || *end != '\0') + { + usage(std::cerr); + return 1; + } + concurrency_limit = value; + } + break; + + case 'R': + do_random = true; + break; + + case 'v': + do_verbose = true; + break; + + case 'h': + case '?': + usage(std::cout); + return 0; + } + } + + if ((optind + 1) != argc) + { + usage(std::cerr); + return 1; + } + + FILE * uuids(fopen(argv[optind], "r")); + if (! uuids) + { + const char * errstr(strerror(errno)); + + std::cerr << "Couldn't open UUID file '" << argv[optind] << "'. Reason: " + << errstr << std::endl; + return 1; + } + + // Initialization + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::startThread(); + + // Get service point + LLCore::HttpRequest * hr = new LLCore::HttpRequest(); + + // Get a handler/working set + WorkingSet ws; + + // Fill the working set with work + ws.mUrl = url_format; + ws.loadTextureUuids(uuids); + ws.mRandomRange = do_random; + ws.mVerbose = do_verbose; + ws.mMaxConcurrency = concurrency_limit; + + if (! ws.mTextures.size()) + { + std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl; + return 1; + } + + // Run it + while (! ws.reload(hr)) + { + hr->update(1000); +#if defined(WIN32) + Sleep(5); +#else + usleep(5000); +#endif + } + + // Report + std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi + << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount + << std::endl; + + // Clean up + delete hr; + term_curl(); + + return 0; +} + + +void usage(std::ostream & out) +{ + out << "\n" + "usage:\thttp_texture_load [options] uuid_file\n" + "\n" + "This is a standalone program to drive the New Platform HTTP Library.\n" + "The program is supplied with a file of texture UUIDs, one per line\n" + "These are fetched sequentially using a pool of concurrent connection\n" + "until all are fetched. The default URL format is only useful from\n" + "within Linden Lab but this can be overriden with a printf-style\n" + "URL formatting string on the command line.\n" + "\n" + "Options:\n" + "\n" + " -u printf-style format string for URL generation\n" + " Default: " << url_format << "\n" + " -R Issue GETs with random Range: headers\n" + " -c Maximum request concurrency. Range: [1..100]\n" + " Default: " << concurrency_limit << "\n" + " -v Verbose mode. Issue some chatter while running\n" + " -h print this help\n" + "\n" + << std::endl; +} + + +WorkingSet::WorkingSet() + : LLCore::HttpHandler(), + mVerbose(false), + mRandomRange(false), + mRemaining(200), + mLimit(200), + mAt(0), + mErrorsApi(0), + mErrorsHttp(0), + mSuccesses(0), + mByteCount(0L) +{ + mTextures.reserve(30000); +} + + +bool WorkingSet::reload(LLCore::HttpRequest * hr) +{ + int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size()))); + + for (int i(0); i < to_do; ++i) + { + char buffer[1024]; +#if defined(WIN32) + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#else + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mUuids[mAt].c_str()); +#endif + int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); + int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); + + LLCore::HttpHandle handle; + if (offset || length) + { + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, NULL, this); + } + else + { + handle = hr->requestGet(0, 0, buffer, NULL, NULL, this); + } + if (! handle) + { + // Fatal. Couldn't queue up something. + std::cerr << "Failed to queue work to HTTP Service. Reason: " + << hr->getStatus().toString() << std::endl; + exit(1); + } + else + { + mHandles.insert(handle); + } + mAt++; + mRemaining--; + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Queued " << count << std::endl; + } + } + + // Are we done? + return (! mRemaining) && mHandles.empty(); +} + + +void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_set_t::iterator it(mHandles.find(handle)); + if (mHandles.end() == it) + { + // Wha? + std::cerr << "Failed to find handle in request list. Fatal." << std::endl; + exit(1); + } + else + { + LLCore::HttpStatus status(response->getStatus()); + if (status) + { + // More success + LLCore::BufferArray * data(response->getBody()); + mByteCount += data->size(); + ++mSuccesses; + } + else + { + // Something in this library or libcurl + if (status.isHttpStatus()) + { + ++mErrorsHttp; + } + else + { + ++mErrorsApi; + } + } + mHandles.erase(it); + } + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Handled " << count << std::endl; + } +} + + +void WorkingSet::loadTextureUuids(FILE * in) +{ + char buffer[1024]; + + while (fgets(buffer, sizeof(buffer), in)) + { + WorkingSet::Spec texture; + char * state(NULL); + char * token = strtok_r(buffer, " \t\n,", &state); + if (token && 36 == strlen(token)) + { + // Close enough for this function + texture.mUuid = token; + texture.mOffset = 0; + texture.mLength = 0; + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int offset(atoi(token)); + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int length(atoi(token)); + texture.mOffset = offset; + texture.mLength = length; + } + } + mTextures.push_back(texture); + } + } + mRemaining = mLimit = mTextures.size(); +} + + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +// Very much a subset of posix functionality. Don't push +// it too hard... +int getopt(int argc, char * const argv[], const char *optstring) +{ + static int pos(0); + while (optind < argc) + { + if (pos == 0) + { + if (argv[optind][0] != '-') + return -1; + pos = 1; + } + if (! argv[optind][pos]) + { + ++optind; + pos = 0; + continue; + } + const char * thing(strchr(optstring, argv[optind][pos])); + if (! thing) + { + ++optind; + return -1; + } + if (thing[1] == ':') + { + optarg = argv[++optind]; + ++optind; + pos = 0; + } + else + { + optarg = NULL; + ++pos; + } + return *thing; + } + return -1; +} + +#endif + -- cgit v1.2.3 From 57575339bb7dd4f67c5e4dc1c1ccc9eda6a2f8f5 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 15 Jun 2012 13:42:13 -0400 Subject: Fix for linux/mac builds. --- indra/llcorehttp/examples/http_texture_load.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 83139912c9..6f79833cf6 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -267,7 +267,7 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr) #if defined(WIN32) _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); #else - snprintf(buffer, sizeof(buffer), mUrl.c_str(), mUuids[mAt].c_str()); + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str()); #endif int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); -- cgit v1.2.3 From 8006203bdae4e78b4eeb54e715190ace58f43556 Mon Sep 17 00:00:00 2001 From: callum Date: Fri, 15 Jun 2012 11:48:41 -0700 Subject: MAINT-1151 FIX [crashhunters] crash on startup in LLPipeline::init() Reviewed by CB --- indra/newview/pipeline.cpp | 176 ++++++++++++++++++++++++--------------------- indra/newview/pipeline.h | 1 + 2 files changed, 94 insertions(+), 83 deletions(-) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 0a4cf63205..2e9112ed81 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -439,6 +439,19 @@ LLPipeline::LLPipeline() : mLightFunc = 0; } +void LLPipeline::connectRefreshCachedSettingsSafe(const std::string name) +{ + LLPointer cntrl_ptr = gSavedSettings.getControl(name); + if ( cntrl_ptr.isNull() ) + { + llwarns << "Global setting name not found:" << name << llendl; + } + else + { + cntrl_ptr->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + } +} + void LLPipeline::init() { LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); @@ -523,89 +536,86 @@ void LLPipeline::init() // // Update all settings to trigger a cached settings refresh // - - gSavedSettings.getControl("RenderAutoMaskAlphaDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAutoMaskAlphaNonDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUseFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarMaxVisible")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayVBUpdate")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("UseOcclusion")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("VertexShaderEnable")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarVP")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("WindLightUseAtmosShaders")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSunWash")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFSAASamples")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderResolutionDivisor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUIBuffer")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSSAO")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowResolutionScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderLocalLights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayCreation")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAnimateRes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("FreezeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("DebugBeaconLineWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightBrightness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightThickness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotLightsInNondeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewAmbientColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMinLuminance")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMaxExtractAlpha")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthAmount")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowLumWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowResolutionPow")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowIterations")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowStrength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDepthOfField")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocusTransitionTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFNumber")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocalLength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFieldOfView")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNoise")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurSize")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOMaxScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOEffect")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffsetError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBiasError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeDepthCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeNormCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowGaussian")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurDistFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredAtmospheric")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderReflectionDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightFadeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOrthoClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNearDist")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowSplitExponent")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowErrorCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowFOVCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraMaxCoF")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraDoFResScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaDeferred"); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaNonDeferred"); + connectRefreshCachedSettingsSafe("RenderUseFarClip"); + connectRefreshCachedSettingsSafe("RenderAvatarMaxVisible"); + connectRefreshCachedSettingsSafe("RenderDelayVBUpdate"); + connectRefreshCachedSettingsSafe("UseOcclusion"); + connectRefreshCachedSettingsSafe("VertexShaderEnable"); + connectRefreshCachedSettingsSafe("RenderAvatarVP"); + connectRefreshCachedSettingsSafe("WindLightUseAtmosShaders"); + connectRefreshCachedSettingsSafe("RenderDeferred"); + connectRefreshCachedSettingsSafe("RenderDeferredSunWash"); + connectRefreshCachedSettingsSafe("RenderFSAASamples"); + connectRefreshCachedSettingsSafe("RenderResolutionDivisor"); + connectRefreshCachedSettingsSafe("RenderUIBuffer"); + connectRefreshCachedSettingsSafe("RenderShadowDetail"); + connectRefreshCachedSettingsSafe("RenderDeferredSSAO"); + connectRefreshCachedSettingsSafe("RenderShadowResolutionScale"); + connectRefreshCachedSettingsSafe("RenderLocalLights"); + connectRefreshCachedSettingsSafe("RenderDelayCreation"); + connectRefreshCachedSettingsSafe("RenderAnimateRes"); + connectRefreshCachedSettingsSafe("FreezeTime"); + connectRefreshCachedSettingsSafe("DebugBeaconLineWidth"); + connectRefreshCachedSettingsSafe("RenderHighlightBrightness"); + connectRefreshCachedSettingsSafe("RenderHighlightColor"); + connectRefreshCachedSettingsSafe("RenderHighlightThickness"); + connectRefreshCachedSettingsSafe("RenderSpotLightsInNondeferred"); + connectRefreshCachedSettingsSafe("PreviewAmbientColor"); + connectRefreshCachedSettingsSafe("PreviewDiffuse0"); + connectRefreshCachedSettingsSafe("PreviewSpecular0"); + connectRefreshCachedSettingsSafe("PreviewDiffuse1"); + connectRefreshCachedSettingsSafe("PreviewSpecular1"); + connectRefreshCachedSettingsSafe("PreviewDiffuse2"); + connectRefreshCachedSettingsSafe("PreviewSpecular2"); + connectRefreshCachedSettingsSafe("PreviewDirection0"); + connectRefreshCachedSettingsSafe("PreviewDirection1"); + connectRefreshCachedSettingsSafe("PreviewDirection2"); + connectRefreshCachedSettingsSafe("RenderGlowMinLuminance"); + connectRefreshCachedSettingsSafe("RenderGlowMaxExtractAlpha"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthAmount"); + connectRefreshCachedSettingsSafe("RenderGlowLumWeights"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthWeights"); + connectRefreshCachedSettingsSafe("RenderGlowResolutionPow"); + connectRefreshCachedSettingsSafe("RenderGlowIterations"); + connectRefreshCachedSettingsSafe("RenderGlowWidth"); + connectRefreshCachedSettingsSafe("RenderGlowStrength"); + connectRefreshCachedSettingsSafe("RenderDepthOfField"); + connectRefreshCachedSettingsSafe("CameraFocusTransitionTime"); + connectRefreshCachedSettingsSafe("CameraFNumber"); + connectRefreshCachedSettingsSafe("CameraFocalLength"); + connectRefreshCachedSettingsSafe("CameraFieldOfView"); + connectRefreshCachedSettingsSafe("RenderShadowNoise"); + connectRefreshCachedSettingsSafe("RenderShadowBlurSize"); + connectRefreshCachedSettingsSafe("RenderSSAOScale"); + connectRefreshCachedSettingsSafe("RenderSSAOMaxScale"); + connectRefreshCachedSettingsSafe("RenderSSAOFactor"); + connectRefreshCachedSettingsSafe("RenderSSAOEffect"); + connectRefreshCachedSettingsSafe("RenderShadowOffsetError"); + connectRefreshCachedSettingsSafe("RenderShadowBiasError"); + connectRefreshCachedSettingsSafe("RenderShadowOffset"); + connectRefreshCachedSettingsSafe("RenderShadowBias"); + connectRefreshCachedSettingsSafe("RenderSpotShadowOffset"); + connectRefreshCachedSettingsSafe("RenderSpotShadowBias"); + connectRefreshCachedSettingsSafe("RenderEdgeDepthCutoff"); + connectRefreshCachedSettingsSafe("RenderEdgeNormCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowGaussian"); + connectRefreshCachedSettingsSafe("RenderShadowBlurDistFactor"); + connectRefreshCachedSettingsSafe("RenderDeferredAtmospheric"); + connectRefreshCachedSettingsSafe("RenderReflectionDetail"); + connectRefreshCachedSettingsSafe("RenderHighlightFadeTime"); + connectRefreshCachedSettingsSafe("RenderShadowClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowOrthoClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowNearDist"); + connectRefreshCachedSettingsSafe("RenderFarClip"); + connectRefreshCachedSettingsSafe("RenderShadowSplitExponent"); + connectRefreshCachedSettingsSafe("RenderShadowErrorCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowFOVCutoff"); + connectRefreshCachedSettingsSafe("CameraOffset"); + connectRefreshCachedSettingsSafe("CameraMaxCoF"); + connectRefreshCachedSettingsSafe("CameraDoFResScale"); + connectRefreshCachedSettingsSafe("RenderAutoHideSurfaceAreaLimit"); } LLPipeline::~LLPipeline() diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 9d56077c55..edcaf1cdc9 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -374,6 +374,7 @@ private: BOOL updateDrawableGeom(LLDrawable* drawable, BOOL priority); void assertInitializedDoError(); bool assertInitialized() { const bool is_init = isInit(); if (!is_init) assertInitializedDoError(); return is_init; }; + void connectRefreshCachedSettingsSafe(const std::string name); public: enum {GPU_CLASS_MAX = 3 }; -- cgit v1.2.3 From 2eedd0c3c0fa8b4360c1402c1fb604010a4a2d82 Mon Sep 17 00:00:00 2001 From: callum Date: Fri, 15 Jun 2012 13:00:49 -0700 Subject: Merge with tip --- indra/llrender/llimagegl.cpp | 2 ++ indra/llrender/llrendertarget.cpp | 36 ++++++++++++++++++++++++++ indra/llrender/llrendertarget.h | 8 ++++-- indra/newview/lldrawable.cpp | 48 ++++++++++++++++++++++++++++++----- indra/newview/lldrawable.h | 2 +- indra/newview/lldrawpool.cpp | 42 ------------------------------ indra/newview/lldrawpool.h | 4 --- indra/newview/lldrawpoolterrain.cpp | 28 ++++++++++++++++++++ indra/newview/lldrawpoolterrain.h | 1 + indra/newview/lldrawpooltree.cpp | 15 +++++++++++ indra/newview/llface.cpp | 22 ++++++++++------ indra/newview/llfloaterland.cpp | 17 ++++++++----- indra/newview/llsidepaneliteminfo.cpp | 2 +- indra/newview/llspatialpartition.cpp | 17 ++++++++++--- indra/newview/llsurface.cpp | 6 +++++ indra/newview/llsurfacepatch.cpp | 14 ++++++---- indra/newview/llviewerdisplay.cpp | 32 ++++++++++++++++++----- indra/newview/llviewermessage.cpp | 2 ++ indra/newview/llviewerobjectlist.cpp | 39 +++++++++++++++++++++++----- indra/newview/llviewerregion.cpp | 3 ++- indra/newview/llviewerwindow.cpp | 3 +++ indra/newview/llvoavatar.cpp | 6 ++--- indra/newview/llvotree.cpp | 6 ++--- indra/newview/llvovolume.cpp | 17 ++++++++++--- indra/newview/llworld.cpp | 5 +++- 25 files changed, 273 insertions(+), 104 deletions(-) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 793fd4be31..fb03b3f956 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -285,9 +285,11 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat) //---------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_STATS("Image Stats"); // static void LLImageGL::updateStats(F32 current_time) { + LLFastTimer t(FTM_IMAGE_UPDATE_STATS); sLastFrameTime = current_time; sBoundTextureMemoryInBytes = sCurBoundTextureMemory; sCurBoundTextureMemory = 0; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 99f0da330c..cc5c232380 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -69,6 +69,42 @@ LLRenderTarget::~LLRenderTarget() release(); } +void LLRenderTarget::resize(U32 resx, U32 resy, U32 color_fmt) +{ + //for accounting, get the number of pixels added/subtracted + S32 pix_diff = (resx*resy)-(mResX*mResY); + + mResX = resx; + mResY = resy; + + for (U32 i = 0; i < mTex.size(); ++i) + { //resize color attachments + gGL.getTexUnit(0)->bindManual(mUsage, mTex[i]); + LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL, false); + sBytesAllocated += pix_diff*4; + } + + if (mDepth) + { //resize depth attachment + if (mStencil) + { + //use render buffers where stencil buffers are in play + glBindRenderbuffer(GL_RENDERBUFFER, mDepth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mResX, mResY); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + else + { + gGL.getTexUnit(0)->bindManual(mUsage, mDepth); + U32 internal_type = LLTexUnit::getInternalType(mUsage); + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + } + + sBytesAllocated += pix_diff*4; + } +} + + bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples) { stop_glerror(); diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 8360458840..e1a51304f1 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -57,8 +57,6 @@ */ -class LLMultisampleBuffer; - class LLRenderTarget { public: @@ -74,6 +72,12 @@ public: //multiple calls will release previously allocated resources bool allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = false, S32 samples = 0); + //resize existing attachments to use new resolution and color format + // CAUTION: if the GL runs out of memory attempting to resize, this render target will be undefined + // DO NOT use for screen space buffers or for scratch space for an image that might be uploaded + // DO use for render targets that resize often and aren't likely to ruin someone's day if they break + void resize(U32 resx, U32 resy, U32 color_fmt); + //add color buffer attachment //limit of 4 color attachments per render target bool addColorAttachment(U32 color_fmt); diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index a42f4fcaa1..563b9b9cab 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -57,6 +57,8 @@ const F32 MIN_SHADOW_CASTER_RADIUS = 2.0f; static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound"); +extern bool gShiftFrame; + //////////////////////// // @@ -716,6 +718,11 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update) return; } + if (gShiftFrame) + { + return; + } + //switch LOD with the spatial group to avoid artifacts //LLSpatialGroup* sg = getSpatialGroup(); @@ -825,14 +832,19 @@ void LLDrawable::shiftPos(const LLVector4a &shift_vector) mXform.setPosition(mVObjp->getPositionAgent()); } - mXform.setRotation(mVObjp->getRotation()); - mXform.setScale(1,1,1); mXform.updateMatrix(); if (isStatic()) { LLVOVolume* volume = getVOVolume(); - if (!volume) + + bool rebuild = (!volume && + getRenderType() != LLPipeline::RENDER_TYPE_TREE && + getRenderType() != LLPipeline::RENDER_TYPE_TERRAIN && + getRenderType() != LLPipeline::RENDER_TYPE_SKY && + getRenderType() != LLPipeline::RENDER_TYPE_GROUND); + + if (rebuild) { gPipeline.markRebuild(this, LLDrawable::REBUILD_ALL, TRUE); } @@ -846,7 +858,7 @@ void LLDrawable::shiftPos(const LLVector4a &shift_vector) facep->mExtents[0].add(shift_vector); facep->mExtents[1].add(shift_vector); - if (!volume && facep->hasGeometry()) + if (rebuild && facep->hasGeometry()) { facep->clearVertexBuffer(); } @@ -957,6 +969,12 @@ void LLDrawable::updateUVMinMax() { } +LLSpatialGroup* LLDrawable::getSpatialGroup() const +{ + llassert((mSpatialGroupp == NULL) ? getBinIndex() == -1 : getBinIndex() != -1); + return mSpatialGroupp; +} + void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp) { /*if (mSpatialGroupp && (groupp != mSpatialGroupp)) @@ -979,6 +997,8 @@ void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp) } mSpatialGroupp = groupp; + + llassert((mSpatialGroupp == NULL) ? getBinIndex() == -1 : getBinIndex() != -1); } LLSpatialPartition* LLDrawable::getSpatialPartition() @@ -1406,6 +1426,11 @@ void LLSpatialBridge::updateDistance(LLCamera& camera_in, bool force_update) markDead(); return; } + + if (gShiftFrame) + { + return; + } if (mDrawable->getVObj()) { @@ -1484,7 +1509,13 @@ void LLSpatialBridge::cleanupReferences() LLDrawable::cleanupReferences(); if (mDrawable) { - mDrawable->setSpatialGroup(NULL); + LLSpatialGroup* group = mDrawable->getSpatialGroup(); + if (group) + { + group->mOctreeNode->remove(mDrawable); + mDrawable->setSpatialGroup(NULL); + } + if (mDrawable->getVObj()) { LLViewerObject::const_child_list_t& child_list = mDrawable->getVObj()->getChildren(); @@ -1495,7 +1526,12 @@ void LLSpatialBridge::cleanupReferences() LLDrawable* drawable = child->mDrawable; if (drawable) { - drawable->setSpatialGroup(NULL); + LLSpatialGroup* group = drawable->getSpatialGroup(); + if (group) + { + group->mOctreeNode->remove(drawable); + drawable->setSpatialGroup(NULL); + } } } } diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index c01a8c3c29..bc4b301ebb 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -197,7 +197,7 @@ public: S32 findReferences(LLDrawable *drawablep); // Not const because of @#$! iterators... void setSpatialGroup(LLSpatialGroup *groupp); - LLSpatialGroup *getSpatialGroup() const { return mSpatialGroupp; } + LLSpatialGroup *getSpatialGroup() const; LLSpatialPartition* getSpatialPartition(); // Statics diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 7c01a63b86..6c0be0a5c2 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -253,48 +253,6 @@ void LLFacePool::dirtyTextures(const std::set& textures { } -// static -S32 LLFacePool::drawLoop(face_array_t& face_list) -{ - S32 res = 0; - if (!face_list.empty()) - { - for (std::vector::iterator iter = face_list.begin(); - iter != face_list.end(); iter++) - { - LLFace *facep = *iter; - res += facep->renderIndexed(); - } - } - return res; -} - -// static -S32 LLFacePool::drawLoopSetTex(face_array_t& face_list, S32 stage) -{ - S32 res = 0; - if (!face_list.empty()) - { - for (std::vector::iterator iter = face_list.begin(); - iter != face_list.end(); iter++) - { - LLFace *facep = *iter; - gGL.getTexUnit(stage)->bind(facep->getTexture(), TRUE) ; - gGL.getTexUnit(0)->activate(); - res += facep->renderIndexed(); - } - } - return res; -} - -void LLFacePool::drawLoop() -{ - if (!mDrawFace.empty()) - { - drawLoop(mDrawFace); - } -} - void LLFacePool::enqueue(LLFace* facep) { mDrawFace.push_back(facep); diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 64774d06df..e0f2da41d7 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -186,10 +186,6 @@ public: void buildEdges(); - static S32 drawLoop(face_array_t& face_list); - static S32 drawLoopSetTex(face_array_t& face_list, S32 stage); - void drawLoop(); - void addFaceReference(LLFace *facep); void removeFaceReference(LLFace *facep); diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index b95d8296fa..7fc78fb382 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -294,6 +294,34 @@ void LLDrawPoolTerrain::renderShadow(S32 pass) //glCullFace(GL_BACK); } + +void LLDrawPoolTerrain::drawLoop() +{ + if (!mDrawFace.empty()) + { + for (std::vector::iterator iter = mDrawFace.begin(); + iter != mDrawFace.end(); iter++) + { + LLFace *facep = *iter; + + LLMatrix4* model_matrix = &(facep->getDrawable()->getRegion()->mRenderMatrix); + + if (model_matrix != gGLLastMatrix) + { + gGLLastMatrix = model_matrix; + gGL.loadMatrix(gGLModelView); + if (model_matrix) + { + gGL.multMatrix((GLfloat*) model_matrix->mMatrix); + } + gPipeline.mMatrixOpCount++; + } + + facep->renderIndexed(); + } + } +} + void LLDrawPoolTerrain::renderFullShader() { // Hack! Get the region that this draw pool is rendering from! diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 283ed87f1a..2163d087e1 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -83,6 +83,7 @@ protected: void renderFull2TU(); void renderFull4TU(); void renderFullShader(); + void drawLoop(); }; #endif // LL_LLDRAWPOOLSIMPLE_H diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 3165a3516c..83f04e45a8 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -37,6 +37,7 @@ #include "llviewershadermgr.h" #include "llrender.h" #include "llviewercontrol.h" +#include "llviewerregion.h" S32 LLDrawPoolTree::sDiffTex = 0; static LLGLSLShader* shader = NULL; @@ -104,8 +105,22 @@ void LLDrawPoolTree::render(S32 pass) { LLFace *face = *iter; LLVertexBuffer* buff = face->getVertexBuffer(); + if(buff) { + LLMatrix4* model_matrix = &(face->getDrawable()->getRegion()->mRenderMatrix); + + if (model_matrix != gGLLastMatrix) + { + gGLLastMatrix = model_matrix; + gGL.loadMatrix(gGLModelView); + if (model_matrix) + { + gGL.multMatrix((GLfloat*) model_matrix->mMatrix); + } + gPipeline.mMatrixOpCount++; + } + buff->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK); buff->drawRange(LLRender::TRIANGLES, 0, buff->getNumVerts()-1, buff->getNumIndices(), 0); gPipeline.addTrianglesDrawn(buff->getNumIndices()); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index c1c68040cd..9acdee702b 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1189,19 +1189,25 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { if (num_indices + (S32) mIndicesIndex > mVertexBuffer->getNumIndices()) { - llwarns << "Index buffer overflow!" << llendl; - llwarns << "Indices Count: " << mIndicesCount - << " VF Num Indices: " << num_indices - << " Indices Index: " << mIndicesIndex - << " VB Num Indices: " << mVertexBuffer->getNumIndices() << llendl; - llwarns << " Face Index: " << f - << " Pool Type: " << mPoolType << llendl; + if (gDebugGL) + { + llwarns << "Index buffer overflow!" << llendl; + llwarns << "Indices Count: " << mIndicesCount + << " VF Num Indices: " << num_indices + << " Indices Index: " << mIndicesIndex + << " VB Num Indices: " << mVertexBuffer->getNumIndices() << llendl; + llwarns << " Face Index: " << f + << " Pool Type: " << mPoolType << llendl; + } return FALSE; } if (num_vertices + mGeomIndex > mVertexBuffer->getNumVerts()) { - llwarns << "Vertex buffer overflow!" << llendl; + if (gDebugGL) + { + llwarns << "Vertex buffer overflow!" << llendl; + } return FALSE; } } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 64684cef99..8f2b028a44 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2227,8 +2227,8 @@ void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata) BOOL allow_damage = !self->mCheckSafe->get(); BOOL allow_fly = self->mCheckFly->get(); BOOL allow_landmark = TRUE; // cannot restrict landmark creation - BOOL allow_group_scripts = self->mCheckGroupScripts->get() || self->mCheckOtherScripts->get(); BOOL allow_other_scripts = self->mCheckOtherScripts->get(); + BOOL allow_group_scripts = self->mCheckGroupScripts->get() || allow_other_scripts; BOOL allow_publish = FALSE; BOOL mature_publish = self->mMatureCtrl->get(); BOOL push_restriction = self->mPushRestrictionCtrl->get(); @@ -2241,11 +2241,16 @@ void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata) LLViewerRegion* region; region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); - if (!allow_other_scripts && region && region->getAllowDamage()) - { - - LLNotificationsUtil::add("UnableToDisableOutsideScripts"); - return; + if (region && region->getAllowDamage()) + { // Damage is allowed on the region - server will always allow scripts + if ( (!allow_other_scripts && parcel->getParcelFlag(PF_ALLOW_OTHER_SCRIPTS)) || + (!allow_group_scripts && parcel->getParcelFlag(PF_ALLOW_GROUP_SCRIPTS)) ) + { // Don't allow turning off "Run Scripts" if damage is allowed in the region + self->mCheckOtherScripts->set(parcel->getParcelFlag(PF_ALLOW_OTHER_SCRIPTS)); // Restore UI to actual settings + self->mCheckGroupScripts->set(parcel->getParcelFlag(PF_ALLOW_GROUP_SCRIPTS)); + LLNotificationsUtil::add("UnableToDisableOutsideScripts"); + return; + } } // Push data into current parcel diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 1ce05da849..92c2863ffd 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -352,7 +352,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) getChildView("BtnCreator")->setEnabled(FALSE); getChildView("LabelCreatorTitle")->setEnabled(FALSE); getChildView("LabelCreatorName")->setEnabled(FALSE); - getChild("LabelCreatorName")->setValue(getString("unknown")); + getChild("LabelCreatorName")->setValue(getString("unknown_multiple")); } //////////////// diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index d995a1a5eb..d82b62dc19 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -68,6 +68,7 @@ const F32 SG_OCCLUSION_FUDGE = 0.25f; #define assert_states_valid(x) #endif +extern bool gShiftFrame; static U32 sZombieGroups = 0; U32 LLSpatialGroup::sNodeCount = 0; @@ -923,7 +924,10 @@ void LLSpatialGroup::shift(const LLVector4a &offset) mObjectExtents[0].add(offset); mObjectExtents[1].add(offset); - if (!mSpatialPartition->mRenderByGroup) + if (!mSpatialPartition->mRenderByGroup && + mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TREE && + mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TERRAIN && + mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_BRIDGE) { setState(GEOM_DIRTY); gPipeline.markRebuild(this, TRUE); @@ -1238,6 +1242,11 @@ void LLSpatialGroup::updateDistance(LLCamera &camera) return; } + if (gShiftFrame) + { + return; + } + #if !LL_RELEASE_FOR_DOWNLOAD if (isState(LLSpatialGroup::OBJECT_DIRTY)) { @@ -1838,12 +1847,14 @@ BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp) { LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION); - drawablep->setSpatialGroup(NULL); - if (!curp->removeObject(drawablep)) { OCT_ERRS << "Failed to remove drawable from octree!" << llendl; } + else + { + drawablep->setSpatialGroup(NULL); + } assert_octree_valid(mOctree); diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index 54a259f725..230e871b49 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -56,6 +56,7 @@ #include "lldrawable.h" extern LLPipeline gPipeline; +extern bool gShiftFrame; LLColor4U MAX_WATER_COLOR(0, 48, 96, 240); @@ -608,6 +609,11 @@ void LLSurface::moveZ(const S32 x, const S32 y, const F32 delta) void LLSurface::updatePatchVisibilities(LLAgent &agent) { + if (gShiftFrame) + { + return; + } + LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(gAgentCamera.getCameraPositionGlobal()); LLSurfacePatch *patchp; diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index 5077c2c7e1..a9ba2bce9c 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -43,6 +43,7 @@ #include "lldrawpool.h" #include "noise.h" +extern bool gShiftFrame; extern U64 gFrameTime; extern LLPipeline gPipeline; @@ -218,7 +219,7 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 pos_agent.mV[VX] += x * mSurfacep->getMetersPerGrid(); pos_agent.mV[VY] += y * mSurfacep->getMetersPerGrid(); pos_agent.mV[VZ] = *(mDataZ + point_offset); - *vertex = pos_agent; + *vertex = pos_agent-mVObjp->getRegion()->getOriginAgent(); LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent(); LLVector3 tex_pos = rel_pos * (1.f/surface_stride); @@ -366,10 +367,13 @@ void LLSurfacePatch::updateCameraDistanceRegion(const LLVector3 &pos_region) { if (LLPipeline::sDynamicLOD) { - LLVector3 dv = pos_region; - dv -= mCenterRegion; - mVisInfo.mDistance = llmax(0.f, (F32)(dv.magVec() - mRadius))/ - llmax(LLVOSurfacePatch::sLODFactor, 0.1f); + if (!gShiftFrame) + { + LLVector3 dv = pos_region; + dv -= mCenterRegion; + mVisInfo.mDistance = llmax(0.f, (F32)(dv.magVec() - mRadius))/ + llmax(LLVOSurfacePatch::sLODFactor, 0.1f); + } } else { diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 000e7404e8..0ad2a6eb9b 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -79,6 +79,7 @@ #include "llpostprocess.h" extern LLPointer gStartTexture; +extern bool gShiftFrame; LLPointer gDisconnectedImagep = NULL; @@ -162,8 +163,11 @@ void display_startup() glClear(GL_DEPTH_BUFFER_BIT); } +static LLFastTimer::DeclareTimer FTM_UPDATE_CAMERA("Update Camera"); + void display_update_camera() { + LLFastTimer t(FTM_UPDATE_CAMERA); LLMemType mt_uc(LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA); // TODO: cut draw distance down if customizing avatar? // TODO: cut draw distance on per-parcel basis? @@ -217,6 +221,11 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_CLASS("Class"); static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_BUMP("Bump"); static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_LIST("List"); static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE_DELETE("Delete"); +static LLFastTimer::DeclareTimer FTM_RESIZE_WINDOW("Resize Window"); +static LLFastTimer::DeclareTimer FTM_HUD_UPDATE("HUD Update"); +static LLFastTimer::DeclareTimer FTM_DISPLAY_UPDATE_GEOM("Update Geom"); +static LLFastTimer::DeclareTimer FTM_TEXTURE_UNBIND("Texture Unbind"); +static LLFastTimer::DeclareTimer FTM_TELEPORT_DISPLAY("Teleport Display"); // Paint the display! void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) @@ -226,6 +235,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if (gWindowResized) { //skip render on frames where window has been resized + LLFastTimer t(FTM_RESIZE_WINDOW); gGL.flush(); glClear(GL_COLOR_BUFFER_BIT); gViewerWindow->getWindow()->swapBuffers(); @@ -362,6 +372,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if (gTeleportDisplay) { + LLFastTimer t(FTM_TELEPORT_DISPLAY); LLAppViewer::instance()->pingMainloopTimeout("Display:Teleport"); const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived. @@ -581,6 +592,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) // *TODO: merge these two methods { + LLFastTimer t(FTM_HUD_UPDATE); LLMemType mt_uh(LLMemType::MTYPE_DISPLAY_UPDATE_HUD); LLHUDManager::getInstance()->updateEffects(); LLHUDObject::updateAll(); @@ -588,6 +600,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) } { + LLFastTimer t(FTM_DISPLAY_UPDATE_GEOM); LLMemType mt_ug(LLMemType::MTYPE_DISPLAY_UPDATE_GEOM); const F32 max_geom_update_time = 0.005f*10.f*gFrameIntervalSeconds; // 50 ms/second update time gPipeline.createObjects(max_geom_update_time); @@ -597,6 +610,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) } gPipeline.updateGL(); + stop_glerror(); S32 water_clip = 0; @@ -928,14 +942,18 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) stop_glerror(); } - for (U32 i = 0; i < gGLManager.mNumTextureImageUnits; i++) - { //dummy cleanup of any currently bound textures - if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE) - { - gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType()); - gGL.getTexUnit(i)->disable(); + { + LLFastTimer t(FTM_TEXTURE_UNBIND); + for (U32 i = 0; i < gGLManager.mNumTextureImageUnits; i++) + { //dummy cleanup of any currently bound textures + if (gGL.getTexUnit(i)->getCurrType() != LLTexUnit::TT_NONE) + { + gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType()); + gGL.getTexUnit(i)->disable(); + } } } + LLAppViewer::instance()->pingMainloopTimeout("Display:RenderFlush"); if (to_texture) @@ -1001,6 +1019,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) display_stats(); LLAppViewer::instance()->pingMainloopTimeout("Display:Done"); + + gShiftFrame = false; } void render_hud_attachments() diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index d72f6cfb59..bc15d90520 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -132,6 +132,7 @@ static const U32 LLREQUEST_PERMISSION_THROTTLE_LIMIT = 5; // requests static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL = 10.0f; // seconds extern BOOL gDebugClicks; +extern bool gShiftFrame; // function prototypes bool check_offer_throttle(const std::string& from_name, bool check_only); @@ -3736,6 +3737,7 @@ void process_avatar_init_complete(LLMessageSystem* msg, void**) void process_agent_movement_complete(LLMessageSystem* msg, void**) { + gShiftFrame = true; gAgentMovementCompleted = true; LLUUID agent_id; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 4a06685994..383d4a7955 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1438,12 +1438,24 @@ void LLViewerObjectList::updateActive(LLViewerObject *objectp) if (active) { //llinfos << "Adding " << objectp->mID << " " << objectp->getPCodeString() << " to active list." << llendl; - llassert(objectp->getListIndex() == -1); - - mActiveObjects.push_back(objectp); - objectp->setListIndex(mActiveObjects.size()-1); + S32 idx = objectp->getListIndex(); + if (idx <= -1) + { + mActiveObjects.push_back(objectp); + objectp->setListIndex(mActiveObjects.size()-1); + objectp->setOnActiveList(TRUE); + } + else + { + llassert(idx < mActiveObjects.size()); + llassert(mActiveObjects[idx] == objectp); - objectp->setOnActiveList(TRUE); + if (idx >= mActiveObjects.size() || + mActiveObjects[idx] != objectp) + { + llwarns << "Invalid object list index detected!" << llendl; + } + } } else { @@ -1522,6 +1534,10 @@ void LLViewerObjectList::onPhysicsFlagsFetchFailure(const LLUUID& object_id) mPendingPhysicsFlags.erase(object_id); } +static LLFastTimer::DeclareTimer FTM_SHIFT_OBJECTS("Shift Objects"); +static LLFastTimer::DeclareTimer FTM_PIPELINE_SHIFT("Pipeline Shift"); +static LLFastTimer::DeclareTimer FTM_REGION_SHIFT("Region Shift"); + void LLViewerObjectList::shiftObjects(const LLVector3 &offset) { // This is called when we shift our origin when we cross region boundaries... @@ -1533,6 +1549,8 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset) return; } + LLFastTimer t(FTM_SHIFT_OBJECTS); + LLViewerObject *objectp; for (vobj_list_t::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) { @@ -1549,8 +1567,15 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset) } } - gPipeline.shiftObjects(offset); - LLWorld::getInstance()->shiftRegions(offset); + { + LLFastTimer t(FTM_PIPELINE_SHIFT); + gPipeline.shiftObjects(offset); + } + + { + LLFastTimer t(FTM_REGION_SHIFT); + LLWorld::getInstance()->shiftRegions(offset); + } } void LLViewerObjectList::repartitionObjects() diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e3cb985ddb..e4108e2cd1 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1461,7 +1461,8 @@ void LLViewerRegion::unpackRegionHandshake() // all of our terrain stuff, by if (compp->getParamsReady()) { - getLand().dirtyAllPatches(); + //this line creates frame stalls on region crossing and removing it appears to have no effect + //getLand().dirtyAllPatches(); } else { diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ca5523dfbd..c5cbc307ed 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4757,8 +4757,11 @@ void LLViewerWindow::requestResolutionUpdate() mResDirty = true; } +static LLFastTimer::DeclareTimer FTM_WINDOW_CHECK_SETTINGS("Window Settings"); + void LLViewerWindow::checkSettings() { + LLFastTimer t(FTM_WINDOW_CHECK_SETTINGS); if (mStatesDirty) { gGL.refreshState(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 414ddc0c24..73c57adedd 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1370,8 +1370,6 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector) const LLVector3& shift = reinterpret_cast(shift_vector); mLastAnimExtents[0] += shift; mLastAnimExtents[1] += shift; - mNeedsImpostorUpdate = TRUE; - mNeedsAnimUpdate = TRUE; } void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) @@ -5066,9 +5064,9 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name ) LLJoint* jointp = NULL; - if (iter == mJointMap.end()) + if (iter == mJointMap.end() || iter->second == NULL) { //search for joint and cache found joint in lookup table - LLJoint* jointp = mRoot.findJoint(name); + jointp = mRoot.findJoint(name); mJointMap[name] = jointp; } else diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 3556bde9a8..337ddfb24d 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -374,7 +374,7 @@ BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) // *TODO: I don't know what's so special about trees // that they don't get REBUILD_POSITION automatically // at a higher level. - const LLVector3 &this_position = getPositionAgent(); + const LLVector3 &this_position = getPositionRegion(); if (this_position != mLastPosition) { gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_POSITION); @@ -843,10 +843,10 @@ void LLVOTree::updateMesh() LLMatrix4 matrix; // Translate to tree base HACK - adjustment in Z plants tree underground - const LLVector3 &pos_agent = getPositionAgent(); + const LLVector3 &pos_region = getPositionRegion(); //gGL.translatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ] - 0.1f); LLMatrix4 trans_mat; - trans_mat.setTranslation(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ] - 0.1f); + trans_mat.setTranslation(pos_region.mV[VX], pos_region.mV[VY], pos_region.mV[VZ] - 0.1f); trans_mat *= matrix; // Rotate to tree position and bend for current trunk/wind diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index d66c47787e..082818b112 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -4702,8 +4702,14 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) if (buff) { llassert(!face->isState(LLFace::RIGGED)); - face->getGeometryVolume(*volume, face->getTEOffset(), - vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex()); + + if (!face->getGeometryVolume(*volume, face->getTEOffset(), + vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex())) + { //something's gone wrong with the vertex buffer accounting, rebuild this group + group->dirtyGeom(); + gPipeline.markRebuild(group, TRUE); + } + if (buff->isLocked()) { @@ -5037,8 +5043,11 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: llassert(!facep->isState(LLFace::RIGGED)); - facep->getGeometryVolume(*volume, te_idx, - vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset,true); + if (!facep->getGeometryVolume(*volume, te_idx, + vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset,true)) + { + llwarns << "Failed to get geometry for face!" << llendl; + } if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) { diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 14867db182..03fef07c11 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -657,7 +657,10 @@ void LLWorld::updateRegions(F32 max_update_time) if (did_one && max_time <= 0.f) break; max_time = llmin(max_time, max_update_time*.1f); - did_one |= regionp->idleUpdate(max_update_time); + if (regionp->idleUpdate(max_update_time)) + { + did_one = TRUE; + } } } -- cgit v1.2.3 From 6193ee6a331e3dfd562400a32a961bad0b8bed12 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 15:50:48 -0400 Subject: First round of basic tuning work (shorter sleeps, larger BufferArray blocks). Beefed up the metrics gathering in http_texture_load to get memory sizes and cpu consumption on windows (still need to implement that on Mac & linux). Ran runs with various idle loops with sleeps from 20 ms down to pure spinning, varied Block allocation size from 1504 to 2^20 bytes. 2ms/2ms/65540 appears to be a good spot under the test conditions (Win7, danu grid, client in Boston). --- indra/llcorehttp/_httpservice.cpp | 2 +- indra/llcorehttp/bufferarray.h | 2 +- indra/llcorehttp/examples/http_texture_load.cpp | 249 +++++++++++++++++++++++- 3 files changed, 244 insertions(+), 9 deletions(-) diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index beba8f08f4..87a78820f5 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -40,7 +40,7 @@ // Tuning parameters -static const int LOOP_SLEEP_NORMAL_MS = 10; // Normal per-loop sleep in milliseconds +static const int LOOP_SLEEP_NORMAL_MS = 2; // Normal per-loop sleep in milliseconds namespace LLCore diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index 72c3e1c669..d3862b45e1 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -74,7 +74,7 @@ private: public: // Internal magic number, may be used by unit tests. - static const size_t BLOCK_ALLOC_SIZE = 1504; + static const size_t BLOCK_ALLOC_SIZE = 65540; /// Appends the indicated data to the BufferArray /// modifying current position and total size. New diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 6f79833cf6..1277dd3353 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -33,6 +33,8 @@ #include #endif +#include "linden_common.h" + #include "httpcommon.h" #include "httprequest.h" #include "httphandler.h" @@ -43,6 +45,8 @@ #include #include +#include "lltimer.h" + void init_curl(); void term_curl(); @@ -65,6 +69,9 @@ int optind(1); #endif + +// Mostly just a container for the texture IDs and fetch +// parameters.... class WorkingSet : public LLCore::HttpHandler { public: @@ -98,11 +105,46 @@ public: texture_list_t mTextures; int mErrorsApi; int mErrorsHttp; + int mErrorsHttp404; + int mErrorsHttp416; + int mErrorsHttp500; + int mErrorsHttp503; int mSuccesses; long mByteCount; }; +// Gather process information while we run. Process +// size, cpu consumed, wallclock time. + +class Metrics +{ +public: + class MetricsImpl; + +public: + Metrics(); + ~Metrics(); + + void init(); + void sample(); + void term(); + +protected: + MetricsImpl * mImpl; + +public: + U64 mMaxVSZ; + U64 mMinVSZ; + U64 mStartWallTime; + U64 mEndWallTime; + U64 mStartUTime; + U64 mEndUTime; + U64 mStartSTime; + U64 mEndSTime; +}; + + // // // @@ -191,22 +233,38 @@ int main(int argc, char** argv) return 1; } + // Setup metrics + Metrics metrics; + metrics.init(); + // Run it + int passes(0); while (! ws.reload(hr)) { - hr->update(1000); -#if defined(WIN32) - Sleep(5); -#else - usleep(5000); -#endif + hr->update(5000); + ms_sleep(2); + if (0 == (++passes % 200)) + { + metrics.sample(); + } } + metrics.sample(); + metrics.term(); // Report std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount << std::endl; - + std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << " HTTP 416 errors: " << ws.mErrorsHttp416 + << " HTTP 500 errors: " << ws.mErrorsHttp500 << " HTTP 503 errors: " << ws.mErrorsHttp503 + << std::endl; + std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime) + << " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime) + << " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime) + << " uS Maximum VSZ: " << metrics.mMaxVSZ + << " Bytes Minimum VSZ: " << metrics.mMinVSZ << " Bytes" + << std::endl; + // Clean up delete hr; term_curl(); @@ -250,6 +308,10 @@ WorkingSet::WorkingSet() mAt(0), mErrorsApi(0), mErrorsHttp(0), + mErrorsHttp404(0), + mErrorsHttp416(0), + mErrorsHttp500(0), + mErrorsHttp503(0), mSuccesses(0), mByteCount(0L) { @@ -333,7 +395,28 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r // Something in this library or libcurl if (status.isHttpStatus()) { + static const LLCore::HttpStatus hs404(404); + static const LLCore::HttpStatus hs416(416); + static const LLCore::HttpStatus hs500(500); + static const LLCore::HttpStatus hs503(503); + ++mErrorsHttp; + if (hs404 == status) + { + ++mErrorsHttp404; + } + else if (hs416 == status) + { + ++mErrorsHttp416; + } + else if (hs500 == status) + { + ++mErrorsHttp500; + } + else if (hs503 == status) + { + ++mErrorsHttp503; + } } else { @@ -492,3 +575,155 @@ int getopt(int argc, char * const argv[], const char *optstring) #endif + + +#if defined(WIN32) + +#define PSAPI_VERSION 1 +#include "windows.h" +#include "psapi.h" + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mStartSTime = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mStartUTime = uli.QuadPart / U64L(10); + metrics->mStartWallTime = totalTime(); + } + + void MetricsImpl::sample(Metrics * metrics) + { + PROCESS_MEMORY_COUNTERS_EX counters; + + GetProcessMemoryInfo(GetCurrentProcess(), + (PROCESS_MEMORY_COUNTERS *) &counters, + sizeof(counters)); + // Okay, PrivateUsage isn't truly VSZ but it will be + // a good tracker for leaks and fragmentation. Work on + // a better estimator later... + SIZE_T vsz(counters.PrivateUsage); + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, U64(vsz)); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); + } + + void MetricsImpl::term(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mEndSTime = uli.QuadPart / U64L(10); + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mEndUTime = uli.QuadPart / U64L(10); + metrics->mEndWallTime = totalTime(); + } + +protected: +}; + +#elif defined(DARWIN) + + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics *) + {} + + void MetricsImpl::sample(Metrics *) + {} + + void MetricsImpl::term(Metrics *) + {} +}; + +#else + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + + ~MetricsImpl() + {} + + void MetricsImpl::init(Metrics *) + {} + + + void MetricsImpl::sample(Metrics *) + {} + + + void MetricsImpl::term(Metrics *) + {} +}; + +#endif // defined(WIN32) + +Metrics::Metrics() + : mMaxVSZ(U64(0)), + mMinVSZ(U64L(0xffffffffffffffff)), + mStartWallTime(U64(0)), + mEndWallTime(U64(0)), + mStartUTime(U64(0)), + mEndUTime(U64(0)), + mStartSTime(U64(0)), + mEndSTime(U64(0)) +{ + mImpl = new MetricsImpl(); +} + + +Metrics::~Metrics() +{ + delete mImpl; + mImpl = NULL; +} + + +void Metrics::init() +{ + mImpl->init(this); +} + + +void Metrics::sample() +{ + mImpl->sample(this); +} + + +void Metrics::term() +{ + mImpl->term(this); +} + + -- cgit v1.2.3 From b27bb47f3ac203c474adbb65245d23c4e97baca9 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 21:51:02 +0000 Subject: Implement metrics collection for Linux. Next: Mac OS X. --- indra/llcorehttp/examples/http_texture_load.cpp | 161 ++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 13 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 1277dd3353..2fa3cefbb1 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -592,7 +592,7 @@ public: ~MetricsImpl() {} - void MetricsImpl::init(Metrics * metrics) + void init(Metrics * metrics) { HANDLE self(GetCurrentProcess()); // Does not have to be closed FILETIME ft_dummy, ft_system, ft_user; @@ -607,7 +607,7 @@ public: metrics->mStartWallTime = totalTime(); } - void MetricsImpl::sample(Metrics * metrics) + void sample(Metrics * metrics) { PROCESS_MEMORY_COUNTERS_EX counters; @@ -622,7 +622,7 @@ public: metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); } - void MetricsImpl::term(Metrics * metrics) + void term(Metrics * metrics) { HANDLE self(GetCurrentProcess()); // Does not have to be closed FILETIME ft_dummy, ft_system, ft_user; @@ -652,13 +652,13 @@ public: ~MetricsImpl() {} - void MetricsImpl::init(Metrics *) + void init(Metrics *) {} - void MetricsImpl::sample(Metrics *) + void sample(Metrics *) {} - void MetricsImpl::term(Metrics *) + void term(Metrics *) {} }; @@ -668,24 +668,159 @@ class Metrics::MetricsImpl { public: MetricsImpl() + : mProcFS(NULL), + mUsecsPerTick(U64L(0)) {} ~MetricsImpl() - {} + { + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } - void MetricsImpl::init(Metrics *) - {} + void init(Metrics * metrics) + { + if (! mProcFS) + { + mProcFS = fopen("/proc/self/stat", "r"); + if (! mProcFS) + { + const int errnum(errno); + LL_ERRS("Main") << "Error opening proc fs: " << strerror(errnum) << LL_ENDL; + } + } + + long ticks_per_sec(sysconf(_SC_CLK_TCK)); + mUsecsPerTick = U64L(1000000) / ticks_per_sec; + U64 usecs_per_sec(mUsecsPerTick * ticks_per_sec); + if (900000 > usecs_per_sec || 1100000 < usecs_per_sec) + { + LL_ERRS("Main") << "Resolution problems using uSecs for ticks" << LL_ENDL; + } + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); - void MetricsImpl::sample(Metrics *) - {} + sample(metrics); + } - void MetricsImpl::term(Metrics *) - {} + void sample(Metrics * metrics) + { + U64 vsz; + if (scanProcFS(NULL, NULL, &vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + + void term(Metrics * metrics) + { + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + + sample(metrics); + + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + +protected: + bool scanProcFS(U64 * utime, U64 * stime, U64 * vsz) + { + if (mProcFS) + { + int i_dummy; + unsigned int ui_dummy; + unsigned long ul_dummy, user_ticks, sys_ticks, vsize; + long l_dummy, rss; + unsigned long long ull_dummy; + char c_dummy; + + char buffer[256]; + + static const char * format("%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld"); + + fseek(mProcFS, 0L, SEEK_SET); + size_t len = fread(buffer, 1, sizeof(buffer) - 1, mProcFS); + if (! len) + { + return false; + } + buffer[len] = '\0'; + if (23 == sscanf(buffer, format, + &i_dummy, // pid + // &s_dummy, // command name + &c_dummy, // state + &i_dummy, // ppid + &i_dummy, // pgrp + &i_dummy, // session + &i_dummy, // terminal + &i_dummy, // terminal group id + &ui_dummy, // flags + &ul_dummy, // minor faults + &ul_dummy, // minor faults in children + &ul_dummy, // major faults + &ul_dummy, // major faults in children + &user_ticks, + &sys_ticks, + &l_dummy, // cutime + &l_dummy, // cstime + &l_dummy, // process priority + &l_dummy, // nice value + &l_dummy, // thread count + &l_dummy, // time to SIGALRM + &ull_dummy, // start time + &vsize, + &rss)) + { + // Looks like we understand the line + if (utime) + { + *utime = user_ticks * mUsecsPerTick; + } + + if (stime) + { + *stime = sys_ticks * mUsecsPerTick; + } + + if (vsz) + { + *vsz = vsize; + } + return true; + } + } + return false; + } + +protected: + FILE * mProcFS; + U64 mUsecsPerTick; + }; + #endif // defined(WIN32) Metrics::Metrics() -- cgit v1.2.3 From 252c297bcc0d9ed8e732d86c9c5886cd63c54f8d Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 16 Jun 2012 19:03:31 -0400 Subject: Add metrics gathering utils for Mac OS X. All platforms have useful numbers now. --- indra/llcorehttp/examples/http_texture_load.cpp | 78 ++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 2fa3cefbb1..7efdf53959 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -577,7 +577,7 @@ int getopt(int argc, char * const argv[], const char *optstring) -#if defined(WIN32) +#if LL_WINDOWS #define PSAPI_VERSION 1 #include "windows.h" @@ -640,8 +640,10 @@ public: protected: }; -#elif defined(DARWIN) +#elif LL_DARWIN +#include +#include class Metrics::MetricsImpl { @@ -652,14 +654,72 @@ public: ~MetricsImpl() {} - void init(Metrics *) - {} + void init(Metrics * metrics) + { + U64 utime, stime; - void sample(Metrics *) - {} + if (getTimes(&utime, &stime)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + sample(metrics); + } - void term(Metrics *) - {} + void sample(Metrics * metrics) + { + U64 vsz; + + if (getVM(&vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + void term(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + } + +protected: + bool getVM(U64 * vsz) + { + task_basic_info task_info_block; + mach_msg_type_number_t task_info_count(TASK_BASIC_INFO_COUNT); + + if (KERN_SUCCESS != task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &task_info_block, + &task_info_count)) + { + return false; + } + * vsz = task_info_block.virtual_size; + return true; + } + + bool getTimes(U64 * utime, U64 * stime) + { + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + return false; + } + * utime = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + * stime = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + return true; + } + }; #else @@ -821,7 +881,7 @@ protected: }; -#endif // defined(WIN32) +#endif // LL_WINDOWS Metrics::Metrics() : mMaxVSZ(U64(0)), -- cgit v1.2.3 From 614148077de03d100103648e7580e064b4be11d4 Mon Sep 17 00:00:00 2001 From: MartinRJ Fayray Date: Mon, 18 Jun 2012 10:19:37 +0200 Subject: STORM-1844: Block and Ignore buttons on llDialog() menus overlap when there is only one button --- indra/newview/lltoastnotifypanel.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index a8060649ba..acbb5cf67d 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -334,32 +334,24 @@ void LLToastNotifyPanel::updateButtonsLayout(const std::vectorgetRect()); - S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH - S32 ignore_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - ignore_btn_rect.getWidth(); - if (ignore_btn_left + ignore_btn_rect.getWidth() > max_width)// make sure that the ignore button is in panel - { - ignore_btn_left = max_width - ignore_btn_rect.getWidth() - 2 * HPAD; - } + S32 ignore_btn_left = max_width - ignore_btn_rect.getWidth(); ignore_btn_rect.setOriginAndSize(ignore_btn_left, BOTTOM_PAD,// always move ignore button at the bottom ignore_btn_rect.getWidth(), ignore_btn_rect.getHeight()); ignore_btn->setRect(ignore_btn_rect); ignore_btn_width = ignore_btn_rect.getWidth(); mControlPanel->addChild(ignore_btn, -1); + mute_btn_pad = 4 * HPAD; //only use a 4 * HPAD padding if an ignore button exists } if (mIsScriptDialog && mute_btn != NULL) { LLRect mute_btn_rect(mute_btn->getRect()); - S32 buttons_per_row = max_width / BUTTON_WIDTH; //assume that h_pad far less than BUTTON_WIDTH // Place mute (Block) button to the left of the ignore button. - S32 mute_btn_left = buttons_per_row * BUTTON_WIDTH + (buttons_per_row - 1) * h_pad - mute_btn_rect.getWidth() - ignore_btn_width - (h_pad / 2); - if (mute_btn_left + mute_btn_rect.getWidth() > max_width) // make sure that the mute button is in panel - { - mute_btn_left = max_width - mute_btn_rect.getWidth() - 2 * HPAD; - } + S32 mute_btn_left = max_width - mute_btn_rect.getWidth() - ignore_btn_width - mute_btn_pad; mute_btn_rect.setOriginAndSize(mute_btn_left, BOTTOM_PAD,// always move mute button at the bottom mute_btn_rect.getWidth(), mute_btn_rect.getHeight()); mute_btn->setRect(mute_btn_rect); -- cgit v1.2.3 From 46662a3010b2c920ae60b4ca246d56e3caee6f6f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 18 Jun 2012 11:16:58 -0400 Subject: Move dtors for refcounted objects to protected access. --- indra/llcorehttp/_httpopcancel.h | 4 +++- indra/llcorehttp/_httpoperation.h | 8 +++++++- indra/llcorehttp/_httpoprequest.h | 4 +++- indra/llcorehttp/_httpopsetget.h | 4 +++- indra/llcorehttp/_httpopsetpriority.h | 2 ++ indra/llcorehttp/_httpreplyqueue.h | 4 +++- indra/llcorehttp/_httprequestqueue.h | 4 +++- indra/llcorehttp/bufferarray.h | 4 +++- indra/llcorehttp/httpheaders.h | 3 ++- indra/llcorehttp/httpoptions.h | 3 ++- indra/llcorehttp/httpresponse.h | 3 ++- 11 files changed, 33 insertions(+), 10 deletions(-) diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 6d1e0f8774..4d927d1aaf 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -51,8 +51,10 @@ class HttpOpCancel : public HttpOperation { public: HttpOpCancel(HttpHandle handle); - virtual ~HttpOpCancel(); +protected: + virtual ~HttpOpCancel(); // Use release() + private: HttpOpCancel(const HttpOpCancel &); // Not defined void operator=(const HttpOpCancel &); // Not defined diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index de4939a0ac..5823c08c7b 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -73,7 +73,9 @@ class HttpOperation : public LLCoreInt::RefCounted { public: HttpOperation(); - virtual ~HttpOperation(); + +protected: + virtual ~HttpOperation(); // Use release() private: HttpOperation(const HttpOperation &); // Not defined @@ -131,6 +133,8 @@ class HttpOpStop : public HttpOperation { public: HttpOpStop(); + +protected: virtual ~HttpOpStop(); private: @@ -152,6 +156,8 @@ class HttpOpNull : public HttpOperation { public: HttpOpNull(); + +protected: virtual ~HttpOpNull(); private: diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index fc2301057c..4643cc3b75 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -54,7 +54,9 @@ class HttpOpRequest : public HttpOperation { public: HttpOpRequest(); - virtual ~HttpOpRequest(); + +protected: + virtual ~HttpOpRequest(); // Use release() private: HttpOpRequest(const HttpOpRequest &); // Not defined diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h index e065eb4c30..efb24855c5 100644 --- a/indra/llcorehttp/_httpopsetget.h +++ b/indra/llcorehttp/_httpopsetget.h @@ -49,7 +49,9 @@ class HttpOpSetGet : public HttpOperation { public: HttpOpSetGet(); - virtual ~HttpOpSetGet(); + +protected: + virtual ~HttpOpSetGet(); // Use release() private: HttpOpSetGet(const HttpOpSetGet &); // Not defined diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index f1e94b6e43..724293ef78 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -47,6 +47,8 @@ class HttpOpSetPriority : public HttpOperation { public: HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); + +protected: virtual ~HttpOpSetPriority(); private: diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h index 28cb1d68b7..4220a09a3b 100644 --- a/indra/llcorehttp/_httpreplyqueue.h +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -63,7 +63,9 @@ class HttpReplyQueue : public LLCoreInt::RefCounted public: /// Caller acquires a Refcount on construction HttpReplyQueue(); - virtual ~HttpReplyQueue(); + +protected: + virtual ~HttpReplyQueue(); // Use release() private: HttpReplyQueue(const HttpReplyQueue &); // Not defined diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index f96bd7520c..26d7d9dca6 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -51,7 +51,9 @@ class HttpRequestQueue : public LLCoreInt::RefCounted protected: /// Caller acquires a Refcount on construction HttpRequestQueue(); - virtual ~HttpRequestQueue(); + +protected: + virtual ~HttpRequestQueue(); // Use release() private: HttpRequestQueue(const HttpRequestQueue &); // Not defined diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index d3862b45e1..d0c51d3c73 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -66,7 +66,9 @@ class BufferArray : public LLCoreInt::RefCounted { public: BufferArray(); - virtual ~BufferArray(); + +protected: + virtual ~BufferArray(); // Use release() private: BufferArray(const BufferArray &); // Not defined diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h index 0b6d82561b..3449daa3a1 100644 --- a/indra/llcorehttp/httpheaders.h +++ b/indra/llcorehttp/httpheaders.h @@ -68,9 +68,10 @@ public: /// to the instance. A call to @see release() will destroy /// the instance. HttpHeaders(); - ~HttpHeaders(); protected: + virtual ~HttpHeaders(); // Use release() + HttpHeaders(const HttpHeaders &); // Not defined void operator=(const HttpHeaders &); // Not defined diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 0b9dfdc1de..78d0aadb2e 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -61,9 +61,10 @@ class HttpOptions : public LLCoreInt::RefCounted public: HttpOptions(); HttpOptions(const HttpOptions &); - virtual ~HttpOptions(); protected: + virtual ~HttpOptions(); // Use release() + void operator=(const HttpOptions &); // Not defined public: diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 5cf3a919f4..5bcd7c4eb8 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -58,9 +58,10 @@ class HttpResponse : public LLCoreInt::RefCounted { public: HttpResponse(); - virtual ~HttpResponse(); protected: + virtual ~HttpResponse(); // Use release() + HttpResponse(const HttpResponse &); // Not defined void operator=(const HttpResponse &); // Not defined -- cgit v1.2.3 From 9b2daf4b19fca22bd74aefab49ef41eb8e06c651 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Mon, 18 Jun 2012 12:48:57 -0400 Subject: add credit in contributions.txt --- doc/contributions.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/contributions.txt b/doc/contributions.txt index c5db396c97..f803e3da06 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -612,6 +612,7 @@ Jonathan Yap STORM-1796 Kadah Coba STORM-1060 + STORM-1843 Jondan Lundquist Josef Munster Josette Windlow -- cgit v1.2.3 From bbf8137ce5b9e53ca8be2aea5c553bff8699b4a7 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Mon, 18 Jun 2012 13:59:22 -0400 Subject: credit for STORM-1844 --- doc/contributions.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/contributions.txt b/doc/contributions.txt index a34380df04..20b0bff07e 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -727,6 +727,8 @@ Marc2 Sands Marianne McCann Marine Kelley STORM-281 +MartinRJ Fayray + STORM-1844 Matthew Anthony Matthew Dowd VWR-1344 -- cgit v1.2.3 From 1cf8e785bad3562fac23feeb2343cfaec1b971bc Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 18 Jun 2012 18:38:24 -0400 Subject: Tidy Texture Console, add cache & resource wait stats, issue stats line to log on exit. With much trial-and-error, cleaned up the banner on the texture console and made everything mostly fit. Added global cache read, cache write and resource wait count events to the console display to show if cache is working. On clean exit, emit a log line to report stats to log file (intended for automated tests, maybe): LLTextureFetch::endThread: CacheReads: 2618, CacheWrites: 117, ResWaits: 0, TotalHTTPReq: 117 --- indra/newview/lltexturefetch.cpp | 57 +++++++++++++++++++++++++++++++++++----- indra/newview/lltexturefetch.h | 17 ++++++++++++ indra/newview/lltextureview.cpp | 34 +++++++++++++++++++----- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 664af02f78..6e14c5fc63 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -550,6 +550,11 @@ private: unsigned int mHttpReplySize; unsigned int mHttpReplyOffset; bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + // State history + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; }; ////////////////////////////////////////////////////////////////////////////// @@ -854,7 +859,10 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mHttpActive(false), mHttpReplySize(0U), mHttpReplyOffset(0U), - mHttpHasResource(false) + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U) { mCanUseNET = mUrl.empty() ; @@ -905,6 +913,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker() unlockWorkMutex(); // -Mw mFetcher->removeFromHTTPQueue(mID); mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } // Locks: Mw @@ -1127,6 +1136,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk + ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, @@ -1136,6 +1146,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -1323,6 +1334,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mState = WAIT_HTTP_RESOURCE2; setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; return false; } mState = SEND_HTTP_REQ; @@ -1646,6 +1658,7 @@ bool LLTextureFetchWorker::doWork(S32 param) U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; + ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, @@ -2168,7 +2181,10 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest(NULL), mHttpOptions(NULL), mHttpHeaders(NULL), - mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U) { mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -2661,12 +2677,13 @@ void LLTextureFetch::startThread() } // Threads: Ttf -// -// This detaches the texture fetch thread from the LLCore -// HTTP library but doesn't stop the thread running in that -// library... void LLTextureFetch::endThread() { + LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; } // Threads: Ttf @@ -3272,6 +3289,34 @@ int LLTextureFetch::getHttpWaitersCount() } +// Threads: T* +void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) +{ + LLMutexLock lock(&mQueueMutex); // +Mfq + + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq + + +// Threads: T* +void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) +{ + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; +} + ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 50e3181623..e17c71113a 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -186,6 +186,17 @@ public: // Threads: T* int getHttpWaitersCount(); // ---------------------------------- + // Stats management + + // Add given counts to the global totals for the states/requests + // Threads: T* + void updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait); + + // Return the global counts + // Threads: T* + void getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait); + + // ---------------------------------- protected: // Threads: T* (but Ttf in practice) @@ -323,6 +334,12 @@ private: typedef std::set wait_http_res_queue_t; wait_http_res_queue_t mHttpWaitResource; // Mfnq + + // Cumulative stats on the states/requests issued by + // textures running through here. + U32 mTotalCacheReadCount; // Mfq + U32 mTotalCacheWriteCount; // Mfq + U32 mTotalResourceWaitCount; // Mfq public: // A probabilistically-correct indicator that the current diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index bb1535d23d..a4227ec2ff 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -76,7 +76,7 @@ static std::string title_string4(" W x H (Dis) Mem"); static S32 title_x1 = 0; static S32 title_x2 = 460; static S32 title_x3 = title_x2 + 40; -static S32 title_x4 = title_x3 + 50; +static S32 title_x4 = title_x3 + 46; static S32 texture_bar_height = 8; //////////////////////////////////////////////////////////////////////////// @@ -349,7 +349,7 @@ void LLTextureBar::draw() // draw the image size at the end { - std::string num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(), + std::string num_str = llformat("%3dx%3d (%2d) %7d", mImagep->getWidth(), mImagep->getHeight(), mImagep->getDiscardLevel(), mImagep->hasGLTexture() ? mImagep->getTextureMemory() : 0); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, title_x4, getRect().getHeight(), color, LLFontGL::LEFT, LLFontGL::TOP); @@ -523,22 +523,42 @@ void LLGLTexMemBar::draw() LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); LLColor4 color; - - std::string text = ""; + // Gray background using completely magic numbers + gGL.color4f(0.f, 0.f, 0.f, 0.25f); + const LLRect & rect(getRect()); + gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + + std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); - text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", + text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB", total_mem, max_total_mem, bound_mem, max_bound_mem, LLRenderTarget::sBytesAllocated/(1024*1024), - LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests); + LLImageRaw::sGlobalRawMemory >> 20, + discard_bias, + cache_usage, + cache_max_usage); //, cache_entries, cache_max_entries + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, + text_color, LLFontGL::LEFT, LLFontGL::TOP); + + U32 cache_read(0U), cache_write(0U), res_wait(0U); + LLAppViewer::getTextureFetch()->getStateStats(&cache_read, &cache_write, &res_wait); + + text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d Cread: %u Cwrite: %u Rwait: %u", + total_texture_downloaded, + total_object_downloaded, + total_http_requests, + cache_read, + cache_write, + res_wait); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); -- cgit v1.2.3 From f0353abe7605778048d69ce3acb8f5ddd5693083 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 15:43:29 -0400 Subject: Implement timeout and retry count options for requests. Pretty straightforward. Still don't like how I'm managing the options block. Struct? Accessors? Can't decide. But the options now speed up the unit test runs even as I add tests. --- indra/llcorehttp/_httpoprequest.cpp | 21 +++- indra/llcorehttp/_httpoprequest.h | 2 +- indra/llcorehttp/httpoptions.cpp | 23 ++-- indra/llcorehttp/httpoptions.h | 29 +++-- indra/llcorehttp/tests/test_httprequest.hpp | 155 +++++++++++++++++++++++-- indra/llcorehttp/tests/test_llcorehttp_peer.py | 4 + 6 files changed, 204 insertions(+), 30 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index f78971d8f2..ce41ebcce0 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -108,7 +108,7 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(5) // *FIXME: Get from policy definitions + mPolicyRetryLimit(5) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @prepareRequest(). @@ -333,6 +333,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, { mProcFlags |= PF_SAVE_HEADERS; } + mPolicyRetryLimit = options->getRetries(); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, 0, 100); mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); } } @@ -371,10 +373,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); mCurlHandle = curl_easy_init(); - // curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, 30); - curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, 30); curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); @@ -493,13 +492,25 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + // Request options + long timeout(30); + if (mReqOptions) + { + timeout = mReqOptions->getTimeout(); + timeout = llclamp(timeout, 0L, 3600L); + } + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); + + // Request headers if (mReqHeaders) { // Caller's headers last to override mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); - + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 4643cc3b75..9278445763 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -155,7 +155,7 @@ public: // Policy data int mPolicyRetries; HttpTime mPolicyRetryAt; - const int mPolicyRetryLimit; + int mPolicyRetryLimit; }; // end class HttpOpRequest diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index 155fbda7f1..c11d89e619 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -34,14 +34,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(0) -{} - - -HttpOptions::HttpOptions(const HttpOptions & rhs) - : RefCounted(true), - mWantHeaders(rhs.mWantHeaders), - mTracing(rhs.mTracing) + mTracing(0), + mTimeout(30), + mRetries(5) {} @@ -61,4 +56,16 @@ void HttpOptions::setTrace(long level) } +void HttpOptions::setTimeout(unsigned int timeout) +{ + mTimeout = timeout; +} + + +void HttpOptions::setRetries(unsigned int retries) +{ + mRetries = retries; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h index 78d0aadb2e..a0b2253c11 100644 --- a/indra/llcorehttp/httpoptions.h +++ b/indra/llcorehttp/httpoptions.h @@ -60,29 +60,44 @@ class HttpOptions : public LLCoreInt::RefCounted { public: HttpOptions(); - HttpOptions(const HttpOptions &); protected: virtual ~HttpOptions(); // Use release() + HttpOptions(const HttpOptions &); // Not defined void operator=(const HttpOptions &); // Not defined public: - void setWantHeaders(); - bool getWantHeaders() const + void setWantHeaders(); + bool getWantHeaders() const { return mWantHeaders; } - void setTrace(long level); - int getTrace() const + void setTrace(int long); + int getTrace() const { return mTracing; } + + void setTimeout(unsigned int timeout); + unsigned int getTimeout() const + { + return mTimeout; + } + + void setRetries(unsigned int retries); + unsigned int getRetries() const + { + return mRetries; + } protected: - bool mWantHeaders; - long int mTracing; + bool mWantHeaders; + int mTracing; + unsigned int mTimeout; + unsigned int mRetries; + }; // end class HttpOptions diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 5b04796c8a..42e4857037 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -30,6 +30,7 @@ #include "bufferarray.h" #include "httphandler.h" #include "httpresponse.h" +#include "httpoptions.h" #include "_httpservice.h" #include "_httprequestqueue.h" @@ -407,7 +408,8 @@ void HttpRequestTestObjectType::test<5>() mHandlerCalls = 0; HttpRequest * req = NULL; - + HttpOptions * opts = NULL; + try { // Get singletons created @@ -421,6 +423,9 @@ void HttpRequestTestObjectType::test<5>() req = new HttpRequest(); ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + opts = new HttpOptions(); + opts->setRetries(1); // Don't try for too long - default retries take about 18S + // Issue a GET that can't connect mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, @@ -428,14 +433,14 @@ void HttpRequestTestObjectType::test<5>() "http://127.0.0.1:2/nothing/here", 0, 0, - NULL, + opts, NULL, &handler); ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); // Run the notification pump. int count(0); - int limit(180); // With retries, can take more than 10 seconds to give up + int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { req->update(1000); @@ -468,7 +473,11 @@ void HttpRequestTestObjectType::test<5>() usleep(100000); } ensure("Thread actually stopped running", HttpService::isStopped()); - + + // release options + opts->release(); + opts = NULL; + // release the request object delete req; req = NULL; @@ -490,6 +499,11 @@ void HttpRequestTestObjectType::test<5>() catch (...) { stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } delete req; HttpRequest::destroyService(); throw; @@ -503,7 +517,7 @@ void HttpRequestTestObjectType::test<6>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET to real service"); @@ -611,7 +625,7 @@ void HttpRequestTestObjectType::test<7>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET with Range: header to real service"); @@ -721,7 +735,7 @@ void HttpRequestTestObjectType::test<8>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest PUT to real service"); @@ -840,7 +854,7 @@ void HttpRequestTestObjectType::test<9>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest POST to real service"); @@ -959,7 +973,7 @@ void HttpRequestTestObjectType::test<10>() ScopedCurlInit ready; std::string url_base(get_base_url()); - std::cerr << "Base: " << url_base << std::endl; + // std::cerr << "Base: " << url_base << std::endl; set_test_name("HttpRequest GET with some tracing"); @@ -1065,6 +1079,129 @@ void HttpRequestTestObjectType::test<10>() } } + +template <> template <> +void HttpRequestTestObjectType::test<11>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET timeout"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(0); // Don't retry + opts->setTimeout(2); + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 8c3ad805b3..5f0116d384 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -31,6 +31,7 @@ $/LicenseInfo$ import os import sys +import time from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler @@ -97,6 +98,9 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): def answer(self, data): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if self.path.find("/sleep/") != -1: + time.sleep(30) + if "fail" not in self.path: response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) debug("success: %s", response) -- cgit v1.2.3 From a50944e078b98435685f04eda0ba93e95d4c61f2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 17:01:02 -0400 Subject: Cleanup: move magic nubmers to new _httpinternal.h header file. --- indra/llcorehttp/CMakeLists.txt | 1 + indra/llcorehttp/_httpinternal.h | 82 ++++++++++++++++++++++++++++++++++ indra/llcorehttp/_httplibcurl.cpp | 17 ++++--- indra/llcorehttp/_httplibcurl.h | 14 ++---- indra/llcorehttp/_httpoperation.cpp | 3 +- indra/llcorehttp/_httpoprequest.cpp | 62 ++++++++++++------------- indra/llcorehttp/_httpoprequest.h | 6 +++ indra/llcorehttp/_httppolicy.cpp | 4 +- indra/llcorehttp/_httppolicy.h | 3 +- indra/llcorehttp/_httppolicyglobal.cpp | 10 +++-- indra/llcorehttp/_httpservice.cpp | 9 ++-- indra/llcorehttp/_refcounted.cpp | 4 +- indra/llcorehttp/bufferarray.cpp | 4 +- indra/llcorehttp/httpcommon.cpp | 2 +- indra/llcorehttp/httpoptions.cpp | 8 ++-- indra/llcorehttp/httprequest.cpp | 1 + indra/llcorehttp/httprequest.h | 6 +-- indra/newview/lltextureview.cpp | 2 +- 18 files changed, 161 insertions(+), 77 deletions(-) create mode 100644 indra/llcorehttp/_httpinternal.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9d8bae973e..acf4c0d6a3 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -53,6 +53,7 @@ set(llcorehttp_HEADER_FILES httpoptions.h httprequest.h httpresponse.h + _httpinternal.h _httplibcurl.h _httpopcancel.h _httpoperation.h diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h new file mode 100644 index 0000000000..bc0bd6a2ab --- /dev/null +++ b/indra/llcorehttp/_httpinternal.h @@ -0,0 +1,82 @@ +/** + * @file httpinternal.h + * @brief Implementation constants and magic numbers + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_INTERNAL_H_ +#define _LLCORE_HTTP_INTERNAL_H_ + + +// If you find this included in a public interface header, +// something wrong is probably happening. + + +namespace LLCore +{ + +// Maxium number of policy classes that can be defined. +// *FIXME: Currently limited to the default class, extend. +const int POLICY_CLASS_LIMIT = 1; + +// Debug/informational tracing. Used both +// as a global option and in per-request traces. +const int TRACE_OFF = 0; +const int TRACE_LOW = 1; +const int TRACE_CURL_HEADERS = 2; +const int TRACE_CURL_BODIES = 3; + +const int TRACE_MIN = TRACE_OFF; +const int TRACE_MAX = TRACE_CURL_BODIES; + +// Request retry limits +const int DEFAULT_RETRY_COUNT = 5; +const int LIMIT_RETRY_MIN = 0; +const int LIMIT_RETRY_MAX = 100; + +const int DEFAULT_HTTP_REDIRECTS = 10; + +// Timeout value used for both connect and protocol exchange. +// Retries and time-on-queue are not included and aren't +// accounted for. +const long DEFAULT_TIMEOUT = 30L; +const long LIMIT_TIMEOUT_MIN = 0L; +const long LIMIT_TIMEOUT_MAX = 3600L; + +// Limits on connection counts +const int DEFAULT_CONNECTIONS = 8; +const int LIMIT_CONNECTIONS_MIN = 1; +const int LIMIT_CONNECTIONS_MAX = 256; + +// Tuning parameters + +// Time worker thread sleeps after a pass through the +// request, ready and active queues. +const int LOOP_SLEEP_NORMAL_MS = 2; + +// Block allocation size (a tuning parameter) is found +// in bufferarray.h. + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index a176dd5b2a..65eb642056 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -31,6 +31,8 @@ #include "_httpoprequest.h" #include "_httppolicy.h" +#include "llhttpstatuscodes.h" + namespace LLCore { @@ -39,7 +41,8 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) : mService(service) { - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + // *FIXME: Use active policy class count later + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { mMultiHandles[policy_class] = 0; } @@ -61,7 +64,7 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (mMultiHandles[policy_class]) { @@ -89,7 +92,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < HttpRequest::POLICY_CLASS_LIMIT; ++policy_class) + for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -144,7 +147,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < HttpRequest::POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle @@ -159,7 +162,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op) curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); op->mCurlActive = true; - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { HttpPolicy & policy(mService->getPolicy()); @@ -208,7 +211,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode } if (op->mStatus) { - int http_status(200); + int http_status(HTTP_OK); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); op->mStatus = LLCore::HttpStatus(http_status); @@ -220,7 +223,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode op->mCurlHandle = NULL; // Tracing - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 16b68bde43..0d0c4cad6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -36,6 +36,7 @@ #include "httprequest.h" #include "_httpservice.h" +#include "_httpinternal.h" namespace LLCore @@ -91,17 +92,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[HttpRequest::POLICY_CLASS_LIMIT]; -}; // end class HttpLibcurl - - -// --------------------------------------- -// Free functions -// --------------------------------------- - - -curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); - + CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; +}; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 5a31bf90e7..0d9553434e 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -33,6 +33,7 @@ #include "_httprequestqueue.h" #include "_httpreplyqueue.h" #include "_httpservice.h" +#include "_httpinternal.h" #include "lltimer.h" @@ -142,7 +143,7 @@ HttpStatus HttpOperation::cancel() void HttpOperation::addAsReply() { - if (mTracing > 0) + if (mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " << static_cast(this) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index ce41ebcce0..04a8e0baff 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -42,6 +42,7 @@ #include "_httppolicy.h" #include "_httppolicyglobal.h" #include "_httplibcurl.h" +#include "_httpinternal.h" #include "llhttpstatuscodes.h" #include "llproxy.h" @@ -73,13 +74,14 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line); -#if defined(WIN32) +#if LL_WINDOWS // Not available on windows where the legacy strtok interface // is thread-safe. char *strtok_r(char *str, const char *delim, char **saveptr); -#endif +#endif // LL_WINDOWS + } @@ -108,7 +110,7 @@ HttpOpRequest::HttpOpRequest() mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), - mPolicyRetryLimit(5) + mPolicyRetryLimit(DEFAULT_RETRY_COUNT) { // *NOTE: As members are added, retry initialization/cleanup // may need to be extended in @prepareRequest(). @@ -334,8 +336,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, mProcFlags |= PF_SAVE_HEADERS; } mPolicyRetryLimit = options->getRetries(); - mPolicyRetryLimit = llclamp(mPolicyRetryLimit, 0, 100); - mTracing = (std::max)(mTracing, llclamp(options->getTrace(), 0, 3)); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, LIMIT_RETRY_MIN, LIMIT_RETRY_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), TRACE_MIN, TRACE_MAX)); } } @@ -384,7 +386,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, 10); // *FIXME: parameterize this later + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); @@ -452,8 +454,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); - mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); } break; @@ -463,7 +463,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } // Tracing - if (mTracing > 1) + if (mTracing >= TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); @@ -478,7 +478,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) char range_line[64]; -#if defined(WIN32) +#if LL_WINDOWS _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); @@ -486,7 +486,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) snprintf(range_line, sizeof(range_line), (mReqLength ? fmt1 : fmt2), (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); -#endif // defined(WIN32) +#endif // LL_WINDOWS range_line[sizeof(range_line) - 1] = '\0'; mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); } @@ -494,11 +494,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); // Request options - long timeout(30); + long timeout(DEFAULT_TIMEOUT); if (mReqOptions) { timeout = mReqOptions->getTimeout(); - timeout = llclamp(timeout, 0L, 3600L); + timeout = llclamp(timeout, LIMIT_TIMEOUT_MIN, LIMIT_TIMEOUT_MAX); } curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); @@ -599,11 +599,11 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi memcpy(hdr_buffer, hdr_data, frag_size); hdr_buffer[frag_size] = '\0'; -#if defined(WIN32) +#if LL_WINDOWS if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) #else if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#endif +#endif // LL_WINDOWS { unsigned int first(0), last(0), length(0); int status; @@ -654,7 +654,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe switch (info) { case CURLINFO_TEXT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "TEXT"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -663,7 +663,7 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADERIN"; escape_libcurl_debug_data(buffer, len, true, safe_line); @@ -672,20 +672,20 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_HEADER_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "HEADEROUT"; - escape_libcurl_debug_data(buffer, len, true, safe_line); + escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line logit = true; } break; case CURLINFO_DATA_IN: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAIN"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -699,11 +699,11 @@ int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffe break; case CURLINFO_DATA_OUT: - if (op->mTracing > 1) + if (op->mTracing >= TRACE_CURL_HEADERS) { tag = "DATAOUT"; logit = true; - if (op->mTracing > 2) + if (op->mTracing >= TRACE_CURL_BODIES) { escape_libcurl_debug_data(buffer, len, false, safe_line); } @@ -755,22 +755,22 @@ int parse_content_range_header(char * buffer, if (! strtok_r(buffer, ": \t", &tok_state)) match = false; if (match && (tok = strtok_r(NULL, " \t", &tok_state))) -#if defined(WIN32) +#if LL_WINDOWS match = 0 == _stricmp("bytes", tok); #else match = 0 == strcasecmp("bytes", tok); -#endif +#endif // LL_WINDOWS if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) match = false; if (match) { unsigned int lcl_first(0), lcl_last(0), lcl_len(0); -#if defined(WIN32) +#if LL_WINDOWS if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) #else if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last || lcl_last >= lcl_len) return -1; @@ -779,11 +779,11 @@ int parse_content_range_header(char * buffer, *length = lcl_len; return 0; } -#if defined(WIN32) +#if LL_WINDOWS if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) #else if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) -#endif +#endif // LL_WINDOWS { if (lcl_first > lcl_last) return -1; @@ -798,14 +798,14 @@ int parse_content_range_header(char * buffer, return 1; } -#if defined(WIN32) +#if LL_WINDOWS char *strtok_r(char *str, const char *delim, char ** savestate) { return strtok_s(str, delim, savestate); } -#endif +#endif // LL_WINDOWS void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 9278445763..f2b709a3a2 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -172,6 +172,12 @@ public: }; // end class HttpOpRequestCompare +// --------------------------------------- +// Free functions +// --------------------------------------- + +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + } // end namespace LLCore #endif // _LLCORE_HTTP_OPREQUEST_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 0e08d88276..4be9f1d45f 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -126,7 +126,7 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) { int active(transport.getActiveCountInClass(policy_class)); - int needed(8 - active); + int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); @@ -242,7 +242,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < HttpRequest::POLICY_CLASS_LIMIT) + if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 4114f64848..05de9303b5 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httpinternal.h" namespace LLCore @@ -108,7 +109,7 @@ protected: HttpRetryQueue mRetryQueue; }; - State mState[HttpRequest::POLICY_CLASS_LIMIT]; + State mState[POLICY_CLASS_LIMIT]; HttpService * mService; // Naked pointer, not refcounted, not owner HttpPolicyGlobal mGlobalOptions; diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp index 6b1de38fd6..ca04839eaf 100644 --- a/indra/llcorehttp/_httppolicyglobal.cpp +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -26,6 +26,8 @@ #include "_httppolicyglobal.h" +#include "_httpinternal.h" + namespace LLCore { @@ -33,8 +35,8 @@ namespace LLCore HttpPolicyGlobal::HttpPolicyGlobal() : mSetMask(0UL), - mConnectionLimit(32L), - mTrace(0), + mConnectionLimit(DEFAULT_CONNECTIONS), + mTrace(TRACE_OFF), mUseLLProxy(0) {} @@ -64,11 +66,11 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) switch (opt) { case HttpRequest::GP_CONNECTION_LIMIT: - mConnectionLimit = value; + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); break; case HttpRequest::GP_TRACE: - mTrace = llclamp(value, 0L, 3L); + mTrace = llclamp(value, long(TRACE_MIN), long(TRACE_MAX)); break; case HttpRequest::GP_LLPROXY: diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 87a78820f5..25f64acc42 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -34,15 +34,12 @@ #include "_httppolicy.h" #include "_httplibcurl.h" #include "_thread.h" +#include "_httpinternal.h" #include "lltimer.h" #include "llthread.h" -// Tuning parameters -static const int LOOP_SLEEP_NORMAL_MS = 2; // Normal per-loop sleep in milliseconds - - namespace LLCore { @@ -230,11 +227,11 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) if (! mExitRequested) { // Setup for subsequent tracing - long tracing(0); + long tracing(TRACE_OFF); mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); op->mTracing = (std::max)(op->mTracing, int(tracing)); - if (op->mTracing > 0) + if (op->mTracing > TRACE_OFF) { LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " << static_cast(op) diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp index 11d75fdf97..e7d0b72741 100644 --- a/indra/llcorehttp/_refcounted.cpp +++ b/indra/llcorehttp/_refcounted.cpp @@ -30,11 +30,11 @@ namespace LLCoreInt { -#if ! defined(WIN32) +#if ! LL_WINDOWS const S32 RefCounted::NOT_REF_COUNTED; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS RefCounted::~RefCounted() {} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index ae92057df0..5eaa60c9ba 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -85,9 +85,9 @@ public: // ================================== -#if ! defined(WIN32) +#if ! LL_WINDOWS const size_t BufferArray::BLOCK_ALLOC_SIZE; -#endif // ! defined(WIN32) +#endif // ! LL_WINDOWS BufferArray::BufferArray() : LLCoreInt::RefCounted(true), diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp index 9f17b5c842..1b18976359 100644 --- a/indra/llcorehttp/httpcommon.cpp +++ b/indra/llcorehttp/httpcommon.cpp @@ -54,9 +54,9 @@ std::string HttpStatus::toHex() const result.fill('0'); result << std::hex << operator unsigned long(); return result.str(); - } + std::string HttpStatus::toString() const { static const char * llcore_errors[] = diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp index c11d89e619..f2771c1f29 100644 --- a/indra/llcorehttp/httpoptions.cpp +++ b/indra/llcorehttp/httpoptions.cpp @@ -26,6 +26,8 @@ #include "httpoptions.h" +#include "_httpinternal.h" + namespace LLCore { @@ -34,9 +36,9 @@ namespace LLCore HttpOptions::HttpOptions() : RefCounted(true), mWantHeaders(false), - mTracing(0), - mTimeout(30), - mRetries(5) + mTracing(TRACE_OFF), + mTimeout(DEFAULT_TIMEOUT), + mRetries(DEFAULT_RETRY_COUNT) {} diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 2036ecfd1c..e906ff8a1e 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -108,6 +108,7 @@ HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::stri HttpRequest::policy_t HttpRequest::createPolicyClass() { + // *FIXME: Implement classes policy_t policy_id = 1; return policy_id; diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 4e78ed3719..24fff24b83 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -102,10 +102,6 @@ public: /// eventual service for any HTTP request. static const int DEFAULT_POLICY_ID = 0; - /// Maximum number of policies that may be defined. No policy - /// ID will equal or exceed this value. - static const int POLICY_CLASS_LIMIT = 1; - enum EGlobalPolicy { /// Maximum number of connections the library will use to @@ -163,7 +159,7 @@ public: /// Create a new policy class into which requests can be made. /// /// @return If positive, the policy_id used to reference - /// the class in other methods. If -1, an error + /// the class in other methods. If 0, an error /// occurred and @see getStatus() may provide more /// detail on the reason. static policy_t createPolicyClass(); diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index a4227ec2ff..ae2da84edd 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public -- cgit v1.2.3 From 6b4fe9fadc2301eb13a2854457b67fbebe045b08 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 19 Jun 2012 17:12:20 -0400 Subject: When a Content-Range header is received, make available the full triplet of . --- indra/llcorehttp/_httpoprequest.cpp | 7 ++++++- indra/llcorehttp/_httpoprequest.h | 1 + indra/llcorehttp/httpresponse.cpp | 1 + indra/llcorehttp/httpresponse.h | 7 +++++-- indra/newview/lltexturefetch.cpp | 4 ++-- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 04a8e0baff..516daadf9b 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -107,6 +107,7 @@ HttpOpRequest::HttpOpRequest() mReplyBody(NULL), mReplyOffset(0), mReplyLength(0), + mReplyFullLength(0), mReplyHeaders(NULL), mPolicyRetries(0), mPolicyRetryAt(HttpTime(0)), @@ -154,6 +155,7 @@ HttpOpRequest::~HttpOpRequest() mReplyOffset = 0; mReplyLength = 0; + mReplyFullLength = 0; if (mReplyBody) { mReplyBody->release(); @@ -224,7 +226,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) if (mReplyOffset || mReplyLength) { // Got an explicit offset/length in response - response->setRange(mReplyOffset, mReplyLength); + response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); } mUserHandler->onCompleted(static_cast(this), response); @@ -362,6 +364,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } mReplyOffset = 0; mReplyLength = 0; + mReplyFullLength = 0; if (mReplyHeaders) { mReplyHeaders->release(); @@ -590,6 +593,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi // taking results from the last header stanza we receive. op->mReplyOffset = 0; op->mReplyLength = 0; + op->mReplyFullLength = 0; op->mStatus = HttpStatus(); } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) @@ -613,6 +617,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi // Success, record the fragment position op->mReplyOffset = first; op->mReplyLength = last - first + 1; + op->mReplyFullLength = length; } else if (-1 == status) { diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index f2b709a3a2..5d2417466c 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -150,6 +150,7 @@ public: BufferArray * mReplyBody; off_t mReplyOffset; size_t mReplyLength; + size_t mReplyFullLength; HttpHeaders * mReplyHeaders; // Policy data diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp index 3dcdadb337..a552e48a1b 100644 --- a/indra/llcorehttp/httpresponse.cpp +++ b/indra/llcorehttp/httpresponse.cpp @@ -37,6 +37,7 @@ HttpResponse::HttpResponse() : LLCoreInt::RefCounted(true), mReplyOffset(0U), mReplyLength(0U), + mReplyFullLength(0U), mBufferArray(NULL), mHeaders(NULL) {} diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 5bcd7c4eb8..925cf81586 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -111,16 +111,18 @@ public: /// If a 'Range:' header was used, these methods are involved /// in setting and returning data about the actual response. - void getRange(unsigned int * offset, unsigned int * length) const + void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const { *offset = mReplyOffset; *length = mReplyLength; + *full = mReplyFullLength; } - void setRange(unsigned int offset, unsigned int length) + void setRange(unsigned int offset, unsigned int length, unsigned int full_length) { mReplyOffset = offset; mReplyLength = length; + mReplyFullLength = full_length; } protected: @@ -128,6 +130,7 @@ protected: HttpStatus mStatus; unsigned int mReplyOffset; unsigned int mReplyLength; + unsigned int mReplyFullLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6e14c5fc63..2eda6a9b80 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1735,8 +1735,8 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe << " status: " << status.toHex() << " '" << status.toString() << "'" << llendl; -// unsigned int offset(0), length(0); -// response->getRange(&offset, &length); +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); // llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle // << " status: " << status.toULong() << " '" << status.toString() << "'" // << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize -- cgit v1.2.3 From 029103133ebac7ee13f1af767f41b012d19a84f5 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 19 Jun 2012 18:08:39 -0500 Subject: MAINT-794 Factor out a lot of CPU overhead around updating objects. --- indra/newview/lldrawable.cpp | 47 ++------- indra/newview/lldrawable.h | 3 +- indra/newview/llflexibleobject.cpp | 13 +-- indra/newview/llselectmgr.cpp | 22 ++-- indra/newview/llviewerobject.cpp | 191 ++++++----------------------------- indra/newview/llviewerobject.h | 17 +--- indra/newview/llviewerobjectlist.cpp | 23 +---- indra/newview/llvoavatar.cpp | 8 +- indra/newview/llvoavatar.h | 2 +- indra/newview/llvoavatarself.cpp | 10 +- indra/newview/llvoavatarself.h | 2 +- indra/newview/llvograss.cpp | 12 +-- indra/newview/llvograss.h | 2 +- indra/newview/llvoground.cpp | 12 +-- indra/newview/llvoground.h | 2 +- indra/newview/llvopartgroup.cpp | 3 +- indra/newview/llvopartgroup.h | 2 +- indra/newview/llvosky.cpp | 3 +- indra/newview/llvosky.h | 2 +- indra/newview/llvotree.cpp | 6 +- indra/newview/llvotree.h | 2 +- indra/newview/llvovolume.cpp | 58 +++++------ indra/newview/llvovolume.h | 2 +- indra/newview/llvowater.cpp | 11 +- indra/newview/llvowater.h | 2 +- indra/newview/llvowlsky.cpp | 4 +- indra/newview/llvowlsky.h | 2 +- indra/newview/pipeline.cpp | 4 + 28 files changed, 115 insertions(+), 352 deletions(-) diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 563b9b9cab..c1f82edaaa 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -99,7 +99,6 @@ void LLDrawable::init() mPositionGroup.clear(); mExtents[0].clear(); mExtents[1].clear(); - mQuietCount = 0; mState = 0; mVObjp = NULL; @@ -407,6 +406,8 @@ void LLDrawable::makeActive() if (!isRoot() && !mParent->isActive()) { mParent->makeActive(); + //NOTE: linked set will now NEVER become static + mParent->setState(LLDrawable::ACTIVE_CHILD); } //all child objects must also be active @@ -424,14 +425,6 @@ void LLDrawable::makeActive() } } - if (mVObjp->getPCode() == LL_PCODE_VOLUME) - { - if (mVObjp->isFlexible()) - { - return; - } - } - if (mVObjp->getPCode() == LL_PCODE_VOLUME) { gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE); @@ -439,28 +432,22 @@ void LLDrawable::makeActive() updatePartition(); } - if (isRoot()) - { - mQuietCount = 0; - } - else - { - getParent()->mQuietCount = 0; - } + llassert(isRoot() || mParent->isActive()); } void LLDrawable::makeStatic(BOOL warning_enabled) { - if (isState(ACTIVE)) + if (isState(ACTIVE) && + !isState(ACTIVE_CHILD) && + !mVObjp->isAttachment() && + !mVObjp->isFlexible()) { clearState(ACTIVE | ANIMATED_CHILD); - if (mParent.notNull() && mParent->isActive() && warning_enabled) - { - LL_WARNS_ONCE("Drawable") << "Drawable becomes static with active parent!" << LL_ENDL; - } - + //drawable became static with active parent, not acceptable + llassert(mParent.isNull() || !mParent->isActive() || !warning_enabled); + LLViewerObject::const_child_list_t& child_list = mVObjp->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); iter != child_list.end(); iter++) @@ -487,8 +474,8 @@ void LLDrawable::makeStatic(BOOL warning_enabled) mSpatialBridge->markDead(); setSpatialBridge(NULL); } + updatePartition(); } - updatePartition(); } // Returns "distance" between target destination and resulting xfrom @@ -792,18 +779,6 @@ void LLDrawable::updateTexture() if (getVOVolume()) { - /*if (isActive()) - { - if (isRoot()) - { - mQuietCount = 0; - } - else - { - getParent()->mQuietCount = 0; - } - }*/ - gPipeline.markRebuild(this, LLDrawable::REBUILD_MATERIAL, TRUE); } } diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index bc4b301ebb..bb0d0d1805 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -281,6 +281,7 @@ public: RIGGED = 0x08000000, PARTITION_MOVE = 0x10000000, ANIMATED_CHILD = 0x20000000, + ACTIVE_CHILD = 0x40000000, } EDrawableFlags; private: //aligned members @@ -294,8 +295,6 @@ public: LLPointer mParent; F32 mDistanceWRTCamera; - - S32 mQuietCount; static S32 getCurrentFrame() { return sCurVisible; } static S32 getMinVisFrameRange(); diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index c4dca4cb79..ef8d11a2e2 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -303,15 +303,10 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 if (drawablep) { //LLFastTimer ftm(FTM_FLEXIBLE_UPDATE); - - //flexible objects never go static - drawablep->mQuietCount = 0; - if (!drawablep->isRoot()) - { - LLViewerObject* parent = (LLViewerObject*) mVO->getParent(); - parent->mDrawable->mQuietCount = 0; - } - + + //ensure drawable is active + drawablep->makeActive(); + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE)) { bool visible = drawablep->isVisible(); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index a55565909f..562ac9fff5 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -390,7 +390,7 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, // don't include an avatar. LLViewerObject* root = obj; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -674,7 +674,7 @@ void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_s // don't include an avatar. LLViewerObject* root = object; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -1387,7 +1387,7 @@ void LLSelectMgr::promoteSelectionToRoot() } LLViewerObject* parentp = object; - while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) + while(parentp->getParent() && !(parentp->isRootEdit())) { parentp = (LLViewerObject*)parentp->getParent(); } @@ -4174,8 +4174,7 @@ struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor virtual bool apply(LLViewerObject* object) { if ( object->permModify() && // preemptive permissions check - object->isRoot() && // don't send for child objects - !object->isJointChild()) + object->isRoot()) // don't send for child objects { object->setFlags( mFlags, mState); } @@ -6036,8 +6035,6 @@ void LLSelectMgr::updateSelectionCenter() // matches the root prim's (affecting the orientation of the manipulators). bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() ); - std::vector < LLViewerObject *> jointed_objects; - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); iter != mSelectedObjects->end(); iter++) { @@ -6055,11 +6052,6 @@ void LLSelectMgr::updateSelectionCenter() } bbox.addBBoxAgent( object->getBoundingBoxAgent() ); - - if (object->isJointChild()) - { - jointed_objects.push_back(object); - } } LLVector3 bbox_center_agent = bbox.getCenterAgent(); @@ -6349,19 +6341,19 @@ void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) bool LLObjectSelection::is_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_valid_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_root_object::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && (object->isRootEdit()); } LLObjectSelection::LLObjectSelection() : diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index af007743bf..4e7055f0bb 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -236,7 +236,6 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mNumFaces(0), mTimeDilation(1.f), mRotTime(0.f), - mJointInfo(NULL), mState(0), mMedia(NULL), mClickAction(0), @@ -281,12 +280,6 @@ LLViewerObject::~LLViewerObject() mInventory = NULL; } - if (mJointInfo) - { - delete mJointInfo; - mJointInfo = NULL; - } - if (mPartSourcep) { mPartSourcep->setDead(); @@ -337,9 +330,6 @@ void LLViewerObject::markDead() if (getParent()) { ((LLViewerObject *)getParent())->removeChild(this); - // go ahead and delete any jointinfo's that we find - delete mJointInfo; - mJointInfo = NULL; } // Mark itself as dead @@ -742,7 +732,7 @@ void LLViewerObject::addThisAndNonJointChildren(std::vector& ob iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; - if ( (!child->isAvatar()) && (!child->isJointChild())) + if ( (!child->isAvatar())) { child->addThisAndNonJointChildren(objects); } @@ -1295,26 +1285,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } } - U8 joint_type = 0; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num); - if (joint_type) - { - // create new joint info - if (!mJointInfo) - { - mJointInfo = new LLVOJointInfo; - } - mJointInfo->mJointType = (EHavokJointType) joint_type; - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num); - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num); - } - else if (mJointInfo) - { - // this joint info is no longer needed - delete mJointInfo; - mJointInfo = NULL; - } - break; } @@ -1962,14 +1932,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, cur_parentp->removeChild(this); - if (mJointInfo && !parent_id) - { - // since this object is no longer parent-relative - // we make sure we delete any joint info - delete mJointInfo; - mJointInfo = NULL; - } - setChanged(MOVED | SILHOUETTE); if (mDrawable.notNull()) @@ -2097,9 +2059,15 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, gPipeline.addDebugBlip(getPositionAgent(), color); } - if ((0.0f == vel_mag_sq) && - (0.0f == accel_mag_sq) && - (0.0f == getAngularVelocity().magVecSquared())) + const F32 MAG_CUTOFF = F_APPROXIMATELY_ZERO; + + llassert(vel_mag_sq >= 0.f); + llassert(accel_mag_sq >= 0.f); + llassert(getAngularVelocity().magVecSquared() >= 0.f); + + if ((MAG_CUTOFF >= vel_mag_sq) && + (MAG_CUTOFF >= accel_mag_sq) && + (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) { mStatic = TRUE; // This object doesn't move! } @@ -2170,117 +2138,36 @@ BOOL LLViewerObject::isActive() const -BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { //static LLFastTimer::DeclareTimer ftm("Viewer Object"); //LLFastTimer t(ftm); - if (mDead) - { - // It's dead. Don't update it. - return TRUE; - } - - // CRO - don't velocity interp linked objects! - // Leviathan - but DO velocity interp joints - if (!mStatic && sVelocityInterpolate && !isSelected()) + if (!mDead) { - // calculate dt from last update - F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); - F32 dt = mTimeDilation * dt_raw; - - if (!mJointInfo) + // CRO - don't velocity interp linked objects! + // Leviathan - but DO velocity interp joints + if (!mStatic && sVelocityInterpolate && !isSelected()) { - applyAngularVelocity(dt); - } + // calculate dt from last update + F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); + F32 dt = mTimeDilation * dt_raw; - LLViewerObject *parentp = (LLViewerObject *) getParent(); - if (mJointInfo) - { - if (parentp) + applyAngularVelocity(dt); + + if (isAttachment()) { - // do parent-relative stuff - LLVector3 ang_vel = getAngularVelocity(); - F32 omega = ang_vel.magVecSquared(); - F32 angle = 0.0f; - LLQuaternion dQ; - if (omega > 0.00001f) - { - omega = sqrt(omega); - angle = omega * dt; - dQ.setQuat(angle, ang_vel); - } - LLVector3 pos = getPosition(); - - if (HJT_HINGE == mJointInfo->mJointType) - { - // hinge = uniform circular motion - LLVector3 parent_pivot = getVelocity(); - LLVector3 parent_axis = getAcceleration(); - - angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame - pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos - pos = mJointInfo->mPivot + pivot_offset; // parent-frame - LLViewerObject::setPosition(pos); - LLQuaternion Q_PC = getRotation(); - setRotation(Q_PC * dQ); - mLastInterpUpdateSecs = time; - } - else if (HJT_POINT == mJointInfo->mJointType) - // || HJT_LPOINT == mJointInfo->mJointType) - { - // point-to-point = spin about axis and uniform circular motion - // of axis about the pivot point - // - // NOTE: this interpolation scheme is not quite good enough to - // reduce the bandwidth -- needs a gravitational correction. - // Similarly for hinges with axes that deviate from vertical. - - LLQuaternion Q_PC = getRotation(); - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - - LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor - pos = mJointInfo->mPivot + pivot_to_child * Q_PC; - LLViewerObject::setPosition(pos); - mLastInterpUpdateSecs = time; - } - /* else if (HJT_WHEEL == mJointInfo->mJointInfo) - { - // wheel = uniform rotation about axis, with linear - // velocity interpolation (if any) - LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame) - - LLQuaternion Q_PC = getRotation(); - - angle = dt * (parent_axis * ang_vel); - dQ.setQuat(angle, parent_axis); - - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - - pos = getPosition() + dt * getVelocity(); - LLViewerObject::setPosition(pos); - mLastInterpUpdateSecs = time; - }*/ + mLastInterpUpdateSecs = time; + return; + } + else + { // Move object based on it's velocity and rotation + interpolateLinearMotion(time, dt); } } - else if (isAttachment()) - { - mLastInterpUpdateSecs = time; - return TRUE; - } - else - { // Move object based on it's velocity and rotation - interpolateLinearMotion(time, dt); - } - } - updateDrawable(FALSE); - - return TRUE; + updateDrawable(FALSE); + } } @@ -3857,15 +3744,6 @@ void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped) ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset); updateDrawable(damped); } - else if (isJointChild()) - { - // compute new parent-relative position - LLViewerObject *parent = (LLViewerObject *) getParent(); - LLQuaternion inv_parent_rot = parent->getRotation(); - inv_parent_rot.transQuat(); - LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot; - LLViewerObject::setPosition(pos_parent, damped); - } else { LLViewerObject::setPosition(pos_edit, damped); @@ -3879,8 +3757,7 @@ LLViewerObject* LLViewerObject::getRootEdit() const { const LLViewerObject* root = this; while (root->mParent - && !(root->mJointInfo - || ((LLViewerObject*)root->mParent)->isAvatar()) ) + && !((LLViewerObject*)root->mParent)->isAvatar()) { root = (LLViewerObject*)root->mParent; } @@ -4621,19 +4498,11 @@ void LLViewerObject::clearIcon() LLViewerObject* LLViewerObject::getSubParent() { - if (isJointChild()) - { - return this; - } return (LLViewerObject*) getParent(); } const LLViewerObject* LLViewerObject::getSubParent() const { - if (isJointChild()) - { - return this; - } return (const LLViewerObject*) getParent(); } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 30c3d03635..19ea06ed20 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -88,18 +88,6 @@ typedef void (*inventory_callback)(LLViewerObject*, S32 serial_num, void*); -// a small struct for keeping track of joints -struct LLVOJointInfo -{ - EHavokJointType mJointType; - LLVector3 mPivot; // parent-frame - // whether the below an axis or anchor (and thus its frame) - // depends on the joint type: - // HINGE ==> axis=parent-frame - // P2P ==> anchor=child-frame - LLVector3 mAxisOrAnchor; -}; - // for exporting textured materials from SL struct LLMaterialExportInfo { @@ -157,7 +145,7 @@ public: LLNameValue* getNVPair(const std::string& name) const; // null if no name value pair by that name // Object create and update functions - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Types of media we can associate enum { MEDIA_NONE = 0, MEDIA_SET = 1 }; @@ -188,8 +176,6 @@ public: virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() - BOOL isJointChild() const { return mJointInfo ? TRUE : FALSE; } - EHavokJointType getJointType() const { return mJointInfo ? mJointInfo->mJointType : HJT_INVALID; } // for jointed and other parent-relative hacks LLViewerObject* getSubParent(); const LLViewerObject* getSubParent() const; @@ -721,7 +707,6 @@ protected: F32 mRotTime; // Amount (in seconds) that object has rotated according to angular velocity (llSetTargetOmega) LLQuaternion mLastRot; // last rotation received from the simulator - LLVOJointInfo* mJointInfo; U8 mState; // legacy LLViewerObjectMedia* mMedia; // NULL if no media associated U8 mClickAction; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 383d4a7955..21a0331f70 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -923,8 +923,6 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) const F64 frame_time = LLFrameTimer::getElapsedSeconds(); - std::vector kill_list; - S32 num_active_objects = 0; LLViewerObject *objectp = NULL; // Make a copy of the list in case something in idleUpdate() messes with it @@ -964,8 +962,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) std::vector::iterator idle_end = idle_list.begin()+idle_count; if (gSavedSettings.getBOOL("FreezeTime")) - { - + { for (std::vector::iterator iter = idle_list.begin(); iter != idle_end; iter++) { @@ -982,21 +979,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) idle_iter != idle_end; idle_iter++) { objectp = *idle_iter; - if (objectp->idleUpdate(agent, world, frame_time)) - { - num_active_objects++; - } - else - { - // If Idle Update returns false, kill object! - kill_list.push_back(objectp); - } - } - for (std::vector::iterator kill_iter = kill_list.begin(); - kill_iter != kill_list.end(); kill_iter++) - { - objectp = *kill_iter; - killObject(objectp); + objectp->idleUpdate(agent, world, frame_time); } } @@ -1066,7 +1049,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) */ LLViewerStats::getInstance()->mNumObjectsStat.addValue((S32) mObjects.size()); - LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(num_active_objects); + LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(idle_count); LLViewerStats::getInstance()->mNumSizeCulledStat.addValue(mNumSizeCulled); LLViewerStats::getInstance()->mNumVisCulledStat.addValue(mNumVisCulled); } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index ade21ff30e..0e117d36e8 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2335,7 +2335,7 @@ void LLVOAvatar::dumpAnimationState() //------------------------------------------------------------------------ // idleUpdate() //------------------------------------------------------------------------ -BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLFastTimer t(FTM_AVATAR_UPDATE); @@ -2343,12 +2343,12 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) if (isDead()) { llinfos << "Warning! Idle on dead avatar" << llendl; - return TRUE; + return; } if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR))) { - return TRUE; + return; } checkTextureLoading() ; @@ -2431,8 +2431,6 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); - - return TRUE; } void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index d106086c88..7e1b8698b0 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -123,7 +123,7 @@ public: U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual BOOL updateLOD(); BOOL updateJointLODs(); void updateLODRiggedAttachments( void ); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index c99e94e60c..482d4d19e0 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -631,15 +631,13 @@ BOOL LLVOAvatarSelf::updateCharacter(LLAgent &agent) } // virtual -BOOL LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (!isAgentAvatarValid()) + if (isAgentAvatarValid()) { - return TRUE; + LLVOAvatar::idleUpdate(agent, world, time); + idleUpdateTractorBeam(); } - LLVOAvatar::idleUpdate(agent, world, time); - idleUpdateTractorBeam(); - return TRUE; } // virtual diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 655fb3a012..3e469e3090 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -75,7 +75,7 @@ protected: //-------------------------------------------------------------------- public: /*virtual*/ void updateRegion(LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); //-------------------------------------------------------------------- // LLCharacter interface and related diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 5ad9ccc9af..566c33c0af 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -277,17 +277,17 @@ BOOL LLVOGrass::isActive() const return TRUE; } -BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS))) { - return TRUE; + return; } if (!mDrawable) { // So drones work. - return TRUE; + return; } if(LLVOTree::isTreeRenderingStopped()) //stop rendering grass @@ -297,14 +297,14 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) mNumBlades = 0 ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); } - return TRUE ; + return; } else if(!mNumBlades)//restart grass rendering { mNumBlades = GRASS_MAX_BLADES ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); - return TRUE ; + return; } if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime())) @@ -312,7 +312,7 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } - return TRUE; + return; } diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h index 00a59facf7..b9835b8802 100644 --- a/indra/newview/llvograss.h +++ b/indra/newview/llvograss.h @@ -73,7 +73,7 @@ public: void plantBlades(); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face = -1, // which face to check, -1 = ALL_SIDES diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp index 6da54435e3..97b7418b40 100644 --- a/indra/newview/llvoground.cpp +++ b/indra/newview/llvoground.cpp @@ -49,18 +49,8 @@ LLVOGround::~LLVOGround() { } -BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GROUND))) - { - return TRUE; - } - - /*if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h index 73b097327e..290579b4da 100644 --- a/indra/newview/llvoground.h +++ b/indra/newview/llvoground.h @@ -41,7 +41,7 @@ protected: public: LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class // later? diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 9cce68fff6..e4f9915e93 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -196,9 +196,8 @@ void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) mDrawable->setPositionGroup(pos); } -BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h index 43b2844f07..42c1252d01 100644 --- a/indra/newview/llvopartgroup.h +++ b/indra/newview/llvopartgroup.h @@ -63,7 +63,7 @@ public: LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual F32 getBinRadius(); virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index 312034022e..31358df85f 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -1052,9 +1052,8 @@ void LLVOSky::calcAtmospherics(void) mFadeColor.setAlpha(0); } -BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } BOOL LLVOSky::updateSky() diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h index 6e6898d80a..2a150eccb9 100644 --- a/indra/newview/llvosky.h +++ b/indra/newview/llvosky.h @@ -461,7 +461,7 @@ public: void cleanupGL(); void restoreGL(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); BOOL updateSky(); // Graphical stuff for objects - maybe broken out into render class diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 337ddfb24d..6687ce432f 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -339,11 +339,11 @@ U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys, return retval; } -BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE))) { - return TRUE; + return; } S32 trunk_LOD = sMAX_NUM_TREE_LOD_LEVELS ; @@ -393,8 +393,6 @@ BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } mTrunkLOD = trunk_LOD; - - return TRUE; } const F32 TREE_BLEND_MIN = 1.f; diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h index 0554935539..52debc85ab 100644 --- a/indra/newview/llvotree.h +++ b/indra/newview/llvotree.h @@ -59,7 +59,7 @@ public: void **user_data, U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class later? /*virtual*/ void render(LLAgent &agent); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 082818b112..510877e2be 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -635,47 +635,35 @@ void LLVOVolume::animateTextures() } } } -BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) -{ - LLViewerObject::idleUpdate(agent, world, time); - - //static LLFastTimer::DeclareTimer ftm("Volume Idle"); - //LLFastTimer t(ftm); - - if (mDead || mDrawable.isNull()) - { - return TRUE; - } - - /////////////////////// - // - // Do texture animation stuff - // - if (mTextureAnimp && gAnimateTextures) +void LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +{ + if (!mDead) { - animateTextures(); - } + if (!mStatic) + { //do some velocity interpolation/rotation + LLViewerObject::idleUpdate(agent, world, time); + } - // Dispatch to implementation - if (mVolumeImpl) - { - mVolumeImpl->doIdleUpdate(agent, world, time); - } + //static LLFastTimer::DeclareTimer ftm("Volume Idle"); + //LLFastTimer t(ftm); + + /////////////////////// + // + // Do texture animation stuff + // - const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40; + if (mTextureAnimp && gAnimateTextures) + { + animateTextures(); + } - if (mDrawable->isActive()) - { - if (mDrawable->isRoot() && - mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && - (!mDrawable->getParent() || !mDrawable->getParent()->isActive())) + // Dispatch to implementation + if (mVolumeImpl) { - mDrawable->makeStatic(); + mVolumeImpl->doIdleUpdate(agent, world, time); } } - - return TRUE; } void LLVOVolume::updateTextures() @@ -916,8 +904,8 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) BOOL LLVOVolume::isActive() const { - return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) || - (mDrawable.notNull() && mDrawable->isActive()); + return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive());// || + //(mDrawable.notNull() && mDrawable->isActive()); } BOOL LLVOVolume::setMaterial(const U8 material) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 5a0960204c..69072607f3 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -114,7 +114,7 @@ public: void deleteFaces(); void animateTextures(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); BOOL isVisible() const ; /*virtual*/ BOOL isActive() const; diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 942eff6171..e8a1c3d1d6 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -100,17 +100,8 @@ void LLVOWater::updateTextures() } // Never gets called -BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - /*if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER))) - { - return TRUE; - } - if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline) diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h index ed709dd840..cf9323ef2e 100644 --- a/indra/newview/llvowater.h +++ b/indra/newview/llvowater.h @@ -58,7 +58,7 @@ public: static void initClass(); static void cleanupClass(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp index afd902201b..a33f42cf84 100644 --- a/indra/newview/llvowlsky.cpp +++ b/indra/newview/llvowlsky.cpp @@ -92,9 +92,9 @@ void LLVOWLSky::initSunDirection(LLVector3 const & sun_direction, { } -BOOL LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; + } BOOL LLVOWLSky::isActive(void) const diff --git a/indra/newview/llvowlsky.h b/indra/newview/llvowlsky.h index 825e13a203..729dced15e 100644 --- a/indra/newview/llvowlsky.h +++ b/indra/newview/llvowlsky.h @@ -53,7 +53,7 @@ public: void initSunDirection(LLVector3 const & sun_direction, LLVector3 const & sun_angular_velocity); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL isActive(void) const; /*virtual*/ LLDrawable * createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index c195f0ea14..ebb28ff239 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1843,6 +1843,10 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); if (done) { + if (drawablep->isRoot()) + { + drawablep->makeStatic(); + } drawablep->clearState(LLDrawable::ON_MOVE_LIST); if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) { //will likely not receive any future world matrix updates -- cgit v1.2.3 From a066bc1994fccae7967921980332505aac97953f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 20 Jun 2012 18:43:28 -0400 Subject: SH-3181 More interface options for API. Also includes returned headers. Only thing interesting in this changeset is the discovery that a sleep in the fake HTTP server ties up tests. Need to thread that or fail on client disconnect or something to speed that up and make it usable for bigger test scenarios. But good enough for now... --- indra/llcorehttp/_httpoprequest.cpp | 20 +++- indra/llcorehttp/tests/test_httprequest.hpp | 156 ++++++++++++++++++++++++- indra/llcorehttp/tests/test_llcorehttp_peer.py | 1 + indra/newview/lltexturefetch.cpp | 5 +- 4 files changed, 175 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 516daadf9b..86ecee5b26 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -595,6 +595,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi op->mReplyLength = 0; op->mReplyFullLength = 0; op->mStatus = HttpStatus(); + if (op->mReplyHeaders) + { + op->mReplyHeaders->mHeaders.clear(); + } } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { @@ -636,9 +640,19 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi if (op->mProcFlags & PF_SAVE_HEADERS) { // Save headers in response - // *FIXME: Implement this... - ; - + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + size_t wanted_size(hdr_size); + if (wanted_size && '\n' == hdr_data[wanted_size - 1]) + { + if (--wanted_size && '\r' == hdr_data[wanted_size - 1]) + { + --wanted_size; + } + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_size)); } return hdr_size; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 42e4857037..cac927cfca 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -29,6 +29,7 @@ #include "httprequest.h" #include "bufferarray.h" #include "httphandler.h" +#include "httpheaders.h" #include "httpresponse.h" #include "httpoptions.h" #include "_httpservice.h" @@ -73,7 +74,8 @@ public: const std::string & name) : mState(state), mName(name), - mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID), + mCheckHeader(false) {} virtual void onCompleted(HttpHandle handle, HttpResponse * response) @@ -95,12 +97,33 @@ public: { mState->mHandlerCalls++; } + if (mCheckHeader) + { + ensure("Response required with header check", response != NULL); + HttpHeaders * header(response->getHeaders()); // Will not hold onto this + ensure("Some quantity of headers returned", header != NULL); + bool found_special(false); + + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (std::string::npos != (*iter).find("X-LL-Special")) + { + found_special = true; + break; + } + } + ensure("Special header X-LL-Special in response", found_special); + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; } HttpRequestTestData * mState; std::string mName; HttpHandle mExpectHandle; + bool mCheckHeader; }; typedef test_group HttpRequestTestGroupType; @@ -1085,6 +1108,135 @@ void HttpRequestTestObjectType::test<11>() { ScopedCurlInit ready; + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with returned headers"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setWantHeaders(); + + // Issue a GET that succeeds + mStatus = HttpStatus(200); + handler.mCheckHeader = true; + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release options + opts->release(); + opts = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckHeader = false; + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + +// *NB: This test must be last. The sleeping webserver +// won't respond for a long time. +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + set_test_name("HttpRequest GET timeout"); // Handler can be stack-allocated *if* there are no dangling @@ -1117,7 +1269,7 @@ void HttpRequestTestObjectType::test<11>() opts->setRetries(0); // Don't retry opts->setTimeout(2); - // Issue a GET that can't connect + // Issue a GET that sleeps mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, 0U, diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 5f0116d384..aedd8acd83 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -107,6 +107,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): self.send_response(200) self.send_header("Content-type", "application/llsd+xml") self.send_header("Content-Length", str(len(response))) + self.send_header("X-LL-Special", "Mememememe"); self.end_headers() self.wfile.write(response) else: # fail requested diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2eda6a9b80..4995d9f5ea 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1043,8 +1043,9 @@ void LLTextureFetchWorker::startWork(S32 param) // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); - static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; // Release waiters while we aren't holding the Mw lock. mFetcher->releaseHttpWaiters(); -- cgit v1.2.3 From 7929db82d1d1db8d4ce7ecbd8436c51dea106f82 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 20 Jun 2012 18:29:18 -0500 Subject: MAINT-794 Move flexi idleUpdate and texture animation to their own update queues. --- indra/newview/llflexibleobject.cpp | 89 +++++++++--- indra/newview/llflexibleobject.h | 10 +- indra/newview/llviewerobjectlist.cpp | 19 ++- indra/newview/llviewertextureanim.cpp | 26 +++- indra/newview/llviewertextureanim.h | 11 +- indra/newview/llvovolume.cpp | 248 +++++++++++++++------------------- indra/newview/llvovolume.h | 5 +- 7 files changed, 245 insertions(+), 163 deletions(-) diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index ef8d11a2e2..06aac5f529 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -44,6 +44,8 @@ #include "llvoavatar.h" /*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f; +std::vector LLVolumeImplFlexible::sInstanceList; +std::vector LLVolumeImplFlexible::sUpdateDelay; static LLFastTimer::DeclareTimer FTM_FLEXIBLE_REBUILD("Rebuild"); static LLFastTimer::DeclareTimer FTM_DO_FLEXIBLE_UPDATE("Update"); @@ -70,8 +72,45 @@ LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectD { mVO->mDrawable->makeActive() ; } + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); + sUpdateDelay.push_back(0); }//----------------------------------------------- +LLVolumeImplFlexible::~LLVolumeImplFlexible() +{ + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + sUpdateDelay[mInstanceIndex] = sUpdateDelay[end_idx]; + } + + sInstanceList.pop_back(); + sUpdateDelay.pop_back(); +} + +//static +void LLVolumeImplFlexible::updateClass() +{ + std::vector::iterator delay_iter = sUpdateDelay.begin(); + + for (std::vector::iterator iter = sInstanceList.begin(); + iter != sInstanceList.end(); + ++iter) + { + --(*delay_iter); + if (*delay_iter <= 0) + { + (*iter)->doIdleUpdate(); + } + ++delay_iter; + } +} + LLVector3 LLVolumeImplFlexible::getFramePosition() const { return mVO->getRenderPosition(); @@ -296,7 +335,7 @@ void LLVolumeImplFlexible::updateRenderRes() // optimization similar to what Havok does for objects that are stationary. //--------------------------------------------------------------------------------- static LLFastTimer::DeclareTimer FTM_FLEXIBLE_UPDATE("Update Flexies"); -void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVolumeImplFlexible::doIdleUpdate() { LLDrawable* drawablep = mVO->mDrawable; @@ -316,31 +355,45 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 updateRenderRes(); gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); } - else if (visible && - !drawablep->isState(LLDrawable::IN_REBUILD_Q1) && - mVO->getPixelArea() > 256.f) + else { - U32 id; F32 pixel_area = mVO->getPixelArea(); - if (mVO->isRootEdit()) + U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; + + if (visible) { - id = mID; + if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1) && + mVO->getPixelArea() > 256.f) + { + U32 id; + + if (mVO->isRootEdit()) + { + id = mID; + } + else + { + LLVOVolume* parent = (LLVOVolume*) mVO->getParent(); + id = parent->getVolumeInterfaceID(); + } + + if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) + { + sUpdateDelay[mInstanceIndex] = (S32) update_period-1; + + updateRenderRes(); + + gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); + } + } } else { - LLVOVolume* parent = (LLVOVolume*) mVO->getParent(); - id = parent->getVolumeInterfaceID(); - } - - U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; - - if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) - { - updateRenderRes(); - gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); + sUpdateDelay[mInstanceIndex] = (S32) update_period; } } + } } } @@ -364,7 +417,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate() if ((mSimulateRes == 0 || !mInitialized) && mVO->mDrawable->isVisible()) { //mVO->markForUpdate(TRUE); - doIdleUpdate(gAgent, *LLWorld::getInstance(), 0.0); + doIdleUpdate(); if (mSimulateRes == 0) { diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h index 56d579d86f..beb281a906 100644 --- a/indra/newview/llflexibleobject.h +++ b/indra/newview/llflexibleobject.h @@ -70,8 +70,16 @@ struct LLFlexibleObjectSection //--------------------------------------------------------- class LLVolumeImplFlexible : public LLVolumeInterface { +private: + static std::vector sInstanceList; + static std::vector sUpdateDelay; + S32 mInstanceIndex; + public: + static void updateClass(); + LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes); + ~LLVolumeImplFlexible(); // Implements LLVolumeInterface U32 getID() const { return mID; } @@ -79,7 +87,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface LLQuaternion getFrameRotation() const; LLVolumeInterfaceType getInterfaceType() const { return INTERFACE_FLEXIBLE; } void updateRenderRes(); - void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void doIdleUpdate(); BOOL doUpdateGeometry(LLDrawable *drawable); LLVector3 getPivotPosition() const; void onSetVolume(const LLVolumeParams &volume_params, const S32 detail); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 21a0331f70..be29621805 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -49,6 +49,8 @@ #include "llstring.h" #include "llhudnametag.h" #include "lldrawable.h" +#include "llflexibleobject.h" +#include "llviewertextureanim.h" #include "xform.h" #include "llsky.h" #include "llviewercamera.h" @@ -941,6 +943,8 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) objectp = *active_iter; if (objectp) { + llassert(objectp->isActive()); + if (idle_count >= idle_list.size()) { idle_list.push_back( objectp ); @@ -979,10 +983,19 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) idle_iter != idle_end; idle_iter++) { objectp = *idle_iter; + llassert(objectp->isActive()); objectp->idleUpdate(agent, world, frame_time); } + + //update flexible objects + LLVolumeImplFlexible::updateClass(); + + //update animated textures + LLViewerTextureAnim::updateClass(); } + + fetchObjectCosts(); fetchPhysicsFlags(); @@ -1402,8 +1415,9 @@ void LLViewerObjectList::removeFromActiveList(LLViewerObject* objectp) { mActiveObjects[idx] = mActiveObjects[last_index]; mActiveObjects[idx]->setListIndex(idx); - mActiveObjects.pop_back(); } + + mActiveObjects.pop_back(); } } @@ -1447,6 +1461,9 @@ void LLViewerObjectList::updateActive(LLViewerObject *objectp) objectp->setOnActiveList(FALSE); } } + + llassert(objectp->isActive() || objectp->getListIndex() == -1); + } void LLViewerObjectList::updateObjectCost(LLViewerObject* object) diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp index 9f1ac7c49c..2b364851a7 100644 --- a/indra/newview/llviewertextureanim.cpp +++ b/indra/newview/llviewertextureanim.cpp @@ -27,21 +27,37 @@ #include "llviewerprecompiledheaders.h" #include "llviewertextureanim.h" +#include "llvovolume.h" #include "llmath.h" #include "llerror.h" -LLViewerTextureAnim::LLViewerTextureAnim() : LLTextureAnim() +std::vector LLViewerTextureAnim::sInstanceList; + +LLViewerTextureAnim::LLViewerTextureAnim(LLVOVolume* vobj) : LLTextureAnim() { + mVObj = vobj; mLastFrame = -1.f; // Force an update initially mLastTime = 0.f; mOffS = mOffT = 0; mScaleS = mScaleT = 1; mRot = 0; + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); } LLViewerTextureAnim::~LLViewerTextureAnim() { + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + } + + sInstanceList.pop_back(); } void LLViewerTextureAnim::reset() @@ -50,6 +66,14 @@ void LLViewerTextureAnim::reset() mTimer.reset(); } +//static +void LLViewerTextureAnim::updateClass() +{ + for (std::vector::iterator iter = sInstanceList.begin(); iter != sInstanceList.end(); ++iter) + { + (*iter)->mVObj->animateTextures(); + } +} S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t, F32 &scale_s, F32 &scale_t, diff --git a/indra/newview/llviewertextureanim.h b/indra/newview/llviewertextureanim.h index dd7bd0cb90..abbfabceb9 100644 --- a/indra/newview/llviewertextureanim.h +++ b/indra/newview/llviewertextureanim.h @@ -30,10 +30,18 @@ #include "lltextureanim.h" #include "llframetimer.h" +class LLVOVolume; + class LLViewerTextureAnim : public LLTextureAnim { +private: + static std::vector sInstanceList; + S32 mInstanceIndex; + public: - LLViewerTextureAnim(); + static void updateClass(); + + LLViewerTextureAnim(LLVOVolume* vobj); virtual ~LLViewerTextureAnim(); /*virtual*/ void reset(); @@ -51,6 +59,7 @@ public: F32 mRot; protected: + LLVOVolume* mVObj; LLFrameTimer mTimer; F64 mLastTime; F32 mLastFrame; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 510877e2be..c46ccd8ad5 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -329,7 +329,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -431,7 +431,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -499,169 +499,142 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, void LLVOVolume::animateTextures() { - F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; - S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); - - if (result) + if (!mDead) { - if (!mTexAnimMode) - { - mFaceMappingChanged = TRUE; - gPipeline.markTextured(mDrawable); - } - mTexAnimMode = result | mTextureAnimp->mMode; - - S32 start=0, end=mDrawable->getNumFaces()-1; - if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) - { - start = end = mTextureAnimp->mFace; - } - - for (S32 i = start; i <= end; i++) + F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; + S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); + + if (result) { - LLFace* facep = mDrawable->getFace(i); - if (!facep) continue; - if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; - - const LLTextureEntry* te = facep->getTextureEntry(); - - if (!te) + if (!mTexAnimMode) { - continue; + mFaceMappingChanged = TRUE; + gPipeline.markTextured(mDrawable); } - - if (!(result & LLViewerTextureAnim::ROTATE)) + mTexAnimMode = result | mTextureAnimp->mMode; + + S32 start=0, end=mDrawable->getNumFaces()-1; + if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) { - te->getRotation(&rot); + start = end = mTextureAnimp->mFace; } - if (!(result & LLViewerTextureAnim::TRANSLATE)) - { - te->getOffset(&off_s,&off_t); - } - if (!(result & LLViewerTextureAnim::SCALE)) + + for (S32 i = start; i <= end; i++) { - te->getScale(&scale_s, &scale_t); - } + LLFace* facep = mDrawable->getFace(i); + if (!facep) continue; + if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; - if (!facep->mTextureMatrix) - { - facep->mTextureMatrix = new LLMatrix4(); - } + const LLTextureEntry* te = facep->getTextureEntry(); + + if (!te) + { + continue; + } + + if (!(result & LLViewerTextureAnim::ROTATE)) + { + te->getRotation(&rot); + } + if (!(result & LLViewerTextureAnim::TRANSLATE)) + { + te->getOffset(&off_s,&off_t); + } + if (!(result & LLViewerTextureAnim::SCALE)) + { + te->getScale(&scale_s, &scale_t); + } - LLMatrix4& tex_mat = *facep->mTextureMatrix; - tex_mat.setIdentity(); - LLVector3 trans ; + if (!facep->mTextureMatrix) + { + facep->mTextureMatrix = new LLMatrix4(); + } + + LLMatrix4& tex_mat = *facep->mTextureMatrix; + tex_mat.setIdentity(); + LLVector3 trans ; - if(facep->isAtlasInUse()) - { - // - //if use atlas for animated texture - //apply the following transform to the animation matrix. - // - - F32 tcoord_xoffset = 0.f ; - F32 tcoord_yoffset = 0.f ; - F32 tcoord_xscale = 1.f ; - F32 tcoord_yscale = 1.f ; if(facep->isAtlasInUse()) { - const LLVector2* tmp = facep->getTexCoordOffset() ; - tcoord_xoffset = tmp->mV[0] ; - tcoord_yoffset = tmp->mV[1] ; + // + //if use atlas for animated texture + //apply the following transform to the animation matrix. + // + + F32 tcoord_xoffset = 0.f ; + F32 tcoord_yoffset = 0.f ; + F32 tcoord_xscale = 1.f ; + F32 tcoord_yscale = 1.f ; + if(facep->isAtlasInUse()) + { + const LLVector2* tmp = facep->getTexCoordOffset() ; + tcoord_xoffset = tmp->mV[0] ; + tcoord_yoffset = tmp->mV[1] ; - tmp = facep->getTexCoordScale() ; - tcoord_xscale = tmp->mV[0] ; - tcoord_yscale = tmp->mV[1] ; - } - trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); + tmp = facep->getTexCoordScale() ; + tcoord_xscale = tmp->mV[0] ; + tcoord_yscale = tmp->mV[1] ; + } + trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); - tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); - } - else //non atlas - { - trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); - tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - } + tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); + } + else //non atlas + { + trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); + tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); + } - LLVector3 scale(scale_s, scale_t, 1.f); - LLQuaternion quat; - quat.setQuat(rot, 0, 0, -1.f); + LLVector3 scale(scale_s, scale_t, 1.f); + LLQuaternion quat; + quat.setQuat(rot, 0, 0, -1.f); - tex_mat.rotate(quat); + tex_mat.rotate(quat); - LLMatrix4 mat; - mat.initAll(scale, LLQuaternion(), LLVector3()); - tex_mat *= mat; + LLMatrix4 mat; + mat.initAll(scale, LLQuaternion(), LLVector3()); + tex_mat *= mat; - tex_mat.translate(trans); + tex_mat.translate(trans); + } } - } - else - { - if (mTexAnimMode && mTextureAnimp->mRate == 0) + else { - U8 start, count; - - if (mTextureAnimp->mFace == -1) - { - start = 0; - count = getNumTEs(); - } - else + if (mTexAnimMode && mTextureAnimp->mRate == 0) { - start = (U8) mTextureAnimp->mFace; - count = 1; - } + U8 start, count; - for (S32 i = start; i < start + count; i++) - { - if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + if (mTextureAnimp->mFace == -1) { - setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + start = 0; + count = getNumTEs(); } - if (mTexAnimMode & LLViewerTextureAnim::SCALE) + else { - setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + start = (U8) mTextureAnimp->mFace; + count = 1; } - if (mTexAnimMode & LLViewerTextureAnim::ROTATE) + + for (S32 i = start; i < start + count; i++) { - setTERotation(i, mTextureAnimp->mRot); + if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + { + setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + } + if (mTexAnimMode & LLViewerTextureAnim::SCALE) + { + setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + } + if (mTexAnimMode & LLViewerTextureAnim::ROTATE) + { + setTERotation(i, mTextureAnimp->mRot); + } } - } - gPipeline.markTextured(mDrawable); - mFaceMappingChanged = TRUE; - mTexAnimMode = 0; - } - } -} - -void LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) -{ - if (!mDead) - { - if (!mStatic) - { //do some velocity interpolation/rotation - LLViewerObject::idleUpdate(agent, world, time); - } - - //static LLFastTimer::DeclareTimer ftm("Volume Idle"); - //LLFastTimer t(ftm); - - /////////////////////// - // - // Do texture animation stuff - // - - if (mTextureAnimp && gAnimateTextures) - { - animateTextures(); - } - - // Dispatch to implementation - if (mVolumeImpl) - { - mVolumeImpl->doIdleUpdate(agent, world, time); + gPipeline.markTextured(mDrawable); + mFaceMappingChanged = TRUE; + mTexAnimMode = 0; + } } } } @@ -904,8 +877,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) BOOL LLVOVolume::isActive() const { - return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive());// || - //(mDrawable.notNull() && mDrawable->isActive()); + return !mStatic; } BOOL LLVOVolume::setMaterial(const U8 material) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 69072607f3..70da9ce510 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -68,7 +68,7 @@ class LLVolumeInterface public: virtual ~LLVolumeInterface() { } virtual LLVolumeInterfaceType getInterfaceType() const = 0; - virtual void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) = 0; + virtual void doIdleUpdate() = 0; virtual BOOL doUpdateGeometry(LLDrawable *drawable) = 0; virtual LLVector3 getPivotPosition() const = 0; virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0; @@ -114,8 +114,7 @@ public: void deleteFaces(); void animateTextures(); - /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); - + BOOL isVisible() const ; /*virtual*/ BOOL isActive() const; /*virtual*/ BOOL isAttachment() const; -- cgit v1.2.3 From 4da93b6ad91dff1de98c3c8dd3674c0544f2958b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 19:45:40 -0400 Subject: SH-3177 Add streambuf/iostream adapters to BufferArray object. Initial version that should have enough of the plumbing to produce a working adapter. Memory test is showing 8 bytes held after one of the tests so I'm going to revisit that later. But basic functionality is there going by the unit tests. --- indra/llcorehttp/CMakeLists.txt | 3 + indra/llcorehttp/bufferarray.cpp | 14 ++ indra/llcorehttp/bufferarray.h | 8 + indra/llcorehttp/bufferstream.cpp | 285 +++++++++++++++++++++++++++ indra/llcorehttp/bufferstream.h | 94 +++++++++ indra/llcorehttp/tests/llcorehttp_test.cpp | 3 +- indra/llcorehttp/tests/test_bufferarray.hpp | 2 +- indra/llcorehttp/tests/test_bufferstream.hpp | 252 +++++++++++++++++++++++ 8 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 indra/llcorehttp/bufferstream.cpp create mode 100644 indra/llcorehttp/bufferstream.h create mode 100644 indra/llcorehttp/tests/test_bufferstream.hpp diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index acf4c0d6a3..4c00cc04e9 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories( set(llcorehttp_SOURCE_FILES bufferarray.cpp + bufferstream.cpp httpcommon.cpp httpheaders.cpp httpoptions.cpp @@ -47,6 +48,7 @@ set(llcorehttp_HEADER_FILES CMakeLists.txt bufferarray.h + bufferstream.h httpcommon.h httphandler.h httpheaders.h @@ -105,6 +107,7 @@ if (LL_TESTS) tests/test_httprequestqueue.hpp tests/test_httpheaders.hpp tests/test_bufferarray.hpp + tests/test_bufferstream.hpp ) set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp index 5eaa60c9ba..5eb8f84c34 100644 --- a/indra/llcorehttp/bufferarray.cpp +++ b/indra/llcorehttp/bufferarray.cpp @@ -288,6 +288,20 @@ int BufferArray::findBlock(size_t pos, size_t * ret_offset) } +bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end) +{ + if (block < 0 || block >= mBlocks.size()) + { + return false; + } + + const Block & b(*mBlocks[block]); + *start = &b.mData[0]; + *end = &b.mData[b.mUsed]; + return true; +} + + // ================================== // BufferArray::Block Definitions // ================================== diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h index d0c51d3c73..1094a435b4 100644 --- a/indra/llcorehttp/bufferarray.h +++ b/indra/llcorehttp/bufferarray.h @@ -37,6 +37,7 @@ namespace LLCore { +class BufferArrayStreamBuf; /// A very simple scatter/gather type map for bulk data. The motivation /// for this class is the writedata callback used by libcurl. Response @@ -65,6 +66,11 @@ namespace LLCore class BufferArray : public LLCoreInt::RefCounted { public: + // BufferArrayStreamBuf has intimate knowledge of this + // implementation to implement a buffer-free adapter. + // Changes here will likely need to be reflected there. + friend class BufferArrayStreamBuf; + BufferArray(); protected: @@ -114,6 +120,8 @@ public: protected: int findBlock(size_t pos, size_t * ret_offset); + + bool getBlockStartEnd(int block, const char ** start, const char ** end); protected: class Block; diff --git a/indra/llcorehttp/bufferstream.cpp b/indra/llcorehttp/bufferstream.cpp new file mode 100644 index 0000000000..6553900eef --- /dev/null +++ b/indra/llcorehttp/bufferstream.cpp @@ -0,0 +1,285 @@ +/** + * @file bufferstream.cpp + * @brief Implements the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferstream.h" + +#include "bufferarray.h" + + +namespace LLCore +{ + +BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) + : mBufferArray(array), + mReadCurPos(0), + mReadCurBlock(-1), + mReadBegin(NULL), + mReadCur(NULL), + mReadEnd(NULL), + mWriteCurPos(0) +{ + if (array) + { + array->addRef(); + mWriteCurPos = array->mLen; + } +} + + +BufferArrayStreamBuf::~BufferArrayStreamBuf() +{ + if (mBufferArray) + { + mBufferArray->release(); + mBufferArray = NULL; + } +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadEnd) + { + // Find the next block with actual data or leave + // mCurBlock/mCur/mEnd unchanged if we're at the end + // of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock + 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + ++new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = mReadCur = new_begin; + mReadEnd = new_end; + } + + return traits_type::to_int_type(*mReadCur); +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() +{ + const int_type ret(underflow()); + + if (traits_type::eof() != ret) + { + ++mReadCur; + ++mReadCurPos; + } + return ret; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadBegin) + { + // Find the previous block with actual data or leave + // mCurBlock/mBegin/mCur/mEnd unchanged if we're at the + // beginning of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock - 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + --new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = new_begin; + mReadEnd = mReadCur = new_end; + } + + if (traits_type::eof() != ch && mReadCur[-1] != ch) + { + return traits_type::eof(); + } + --mReadCurPos; + return traits_type::to_int_type(*--mReadCur); +} + + +std::streamsize BufferArrayStreamBuf::showmanyc() +{ + if (! mBufferArray) + { + return -1; + } + return mBufferArray->mLen - mReadCurPos; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return traits_type::eof(); + } + const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); + mWriteCurPos += wrote; + return wrote ? c : traits_type::eof(); +} + + +std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return 0; + } + const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); + mWriteCurPos += wrote; + return wrote; +} + + +std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) +{ + std::streampos ret(-1); + + if (! mBufferArray) + { + return ret; + } + + if (std::ios_base::in == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mReadCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos >= mBufferArray->size()) + { + pos = (std::max)(size_t(0), mBufferArray->size() - 1); + } + size_t ba_offset(0); + int block(mBufferArray->findBlock(pos, &ba_offset)); + if (block < 0) + return ret; + const char * start(NULL), * end(NULL); + if (! mBufferArray->getBlockStartEnd(block, &start, &end)) + return ret; + mReadCurBlock = block; + mReadBegin = start; + mReadCur = start + ba_offset; + mReadEnd = end; + ret = mReadCurPos = pos; + } + else if (std::ios_base::out == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mWriteCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos < 0) + return ret; + if (pos > mBufferArray->size()) + { + pos = mBufferArray->size(); + } + ret = mWriteCurPos = pos; + } + + return ret; +} + + +BufferArrayStream::BufferArrayStream(BufferArray * ba) + : std::iostream(&mStreamBuf), + mStreamBuf(ba) +{} + + +BufferArrayStream::~BufferArrayStream() +{} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h new file mode 100644 index 0000000000..60bda9ff9a --- /dev/null +++ b/indra/llcorehttp/bufferstream.h @@ -0,0 +1,94 @@ +/** + * @file bufferstream.h + * @brief Public-facing declaration for the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_STREAM_H_ +#define _LLCORE_BUFFER_STREAM_H_ + + +#include +#include + +#include "bufferarray.h" + + +namespace LLCore +{ + + +class BufferArrayStreamBuf : public std::streambuf +{ +public: + BufferArrayStreamBuf(BufferArray * array); + virtual ~BufferArrayStreamBuf(); + +private: + BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined + void operator=(const BufferArrayStreamBuf &); // Not defined + +public: + // Input interfaces from std::streambuf + int_type underflow(); + int_type uflow(); + int_type pbackfail(int_type ch); + std::streamsize showmanyc(); + + // Output interfaces from std::streambuf + int_type overflow(int c); + std::streamsize xsputn(const char * src, std::streamsize count); + + // Common/misc interfaces from std::streambuf + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which); + +protected: + BufferArray * mBufferArray; // Ref counted + size_t mReadCurPos; + int mReadCurBlock; + const char * mReadBegin; + const char * mReadCur; + const char * mReadEnd; + size_t mWriteCurPos; + +}; // end class BufferArrayStreamBuf + + +class BufferArrayStream : public std::iostream +{ +public: + BufferArrayStream(BufferArray * ba); + ~BufferArrayStream(); + +protected: + BufferArrayStream(const BufferArrayStream &); + void operator=(const BufferArrayStream &); + +protected: + BufferArrayStreamBuf mStreamBuf; +}; // end class BufferArrayStream + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index 2b36d3a982..e863ddd13f 100644 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -36,12 +36,13 @@ #include "../test/lltut.h" // Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp" #include "test_httpstatus.hpp" #include "test_refcounted.hpp" #include "test_httpoperation.hpp" #include "test_httprequest.hpp" #include "test_httpheaders.hpp" -#include "test_bufferarray.hpp" #include "test_httprequestqueue.hpp" #include "llproxy.h" diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp index 3f947db967..8a2a64d970 100644 --- a/indra/llcorehttp/tests/test_bufferarray.hpp +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -33,7 +33,7 @@ #include "test_allocator.h" -using namespace LLCoreInt; +using namespace LLCore; diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100644 index 0000000000..5afaba0966 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,252 @@ +/** + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include + +#include "test_allocator.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ + set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL + ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); + ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); + ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); + ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); + ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); + ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); + ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ + set_test_name("BufferArrayStream construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStream * bas = new BufferArrayStream(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL here + ensure("eof() is false on NULL", ! bas->eof()); + ensure("fail() is false on NULL", ! bas->fail()); + ensure("good() on NULL", bas->good()); + + // release the implicit reference, causing the object to be released + delete bas; + bas = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ + set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ + set_test_name("BufferArrayStream construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ + set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + const char * content("This is a string. A fragment."); + const size_t c_len(strlen(content)); + ba->append(content, c_len); + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // Various static state + ensure("underflow() returns 'T'", bsb->underflow() == 'T'); + ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); + ensure("uflow() returns 'T'", bsb->uflow() == 'T'); + ensure("uflow() returns 'h'", bsb->uflow() == 'h'); + ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); + ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); + ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); + ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); + ensure("overflow() appends", bsb->overflow('c') == 'c'); + ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); + ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); + ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); + ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); + ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); + ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); + ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); + ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ + set_test_name("BufferArrayStream construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + //const char * content("This is a string. A fragment."); + //const size_t c_len(strlen(content)); + //ba->append(content, strlen(content)); + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Basic operations + bas << "Hello" << 27 << "."; + ensure("BA length 8", ba->size() == 8); + + std::string str; + bas >> str; + ensure("reads correctly", str == "Hello27."); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); + static U64 mem = GetMemTotal(); +} + + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_STREAM_H_ -- cgit v1.2.3 From eed28348f2668c93bc572cffd8a284e65228ed02 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 20:02:24 -0400 Subject: Compiler warning fix on linux. --- indra/llcorehttp/tests/test_bufferstream.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 5afaba0966..45ddb7fd80 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -242,7 +242,7 @@ void BufferStreamTestObjectType::test<6>() // make sure we didn't leak any memory // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); - static U64 mem = GetMemTotal(); + // static U64 mem = GetMemTotal(); } -- cgit v1.2.3 From ed5db306545e414a1c975c1fff5908b6c2fe1389 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 21 Jun 2012 21:32:33 -0400 Subject: Preparing for better shutdown/cleanup logic. --- indra/llcorehttp/_httprequestqueue.cpp | 29 +++++++++++-- indra/llcorehttp/_httprequestqueue.h | 11 ++++- indra/llcorehttp/_httpservice.cpp | 2 + indra/llcorehttp/httprequest.cpp | 67 ++++++++++++++++++++++++----- indra/llcorehttp/tests/test_httprequest.hpp | 14 +++--- 5 files changed, 100 insertions(+), 23 deletions(-) diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index 92bb5ec5c1..6487ef6fa5 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -39,7 +39,8 @@ HttpRequestQueue * HttpRequestQueue::sInstance(NULL); HttpRequestQueue::HttpRequestQueue() - : RefCounted(true) + : RefCounted(true), + mQueueStopped(false) { } @@ -72,14 +73,25 @@ void HttpRequestQueue::term() } -void HttpRequestQueue::addOp(HttpOperation * op) +HttpStatus HttpRequestQueue::addOp(HttpOperation * op) { + bool wake(false); { HttpScopedLock lock(mQueueMutex); + if (mQueueStopped) + { + // Return op and error to caller + return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); + } + wake = mQueue.empty(); mQueue.push_back(op); } - mQueueCV.notify_all(); + if (wake) + { + mQueueCV.notify_all(); + } + return HttpStatus(); } @@ -129,4 +141,15 @@ void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) return; } + +void HttpRequestQueue::stopQueue() +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueueStopped = true; + } +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 26d7d9dca6..6e8f00c4da 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -30,6 +30,7 @@ #include +#include "httpcommon.h" #include "_refcounted.h" #include "_mutex.h" @@ -74,11 +75,11 @@ public: /// Insert an object at the back of the reply queue. /// - /// Caller my provide one refcount to the Library which takes + /// Caller must provide one refcount to the queue which takes /// possession of the count. /// /// Threading: callable by any thread. - void addOp(HttpOperation * op); + HttpStatus addOp(HttpOperation * op); /// Caller acquires reference count on returned operation /// @@ -89,6 +90,11 @@ public: /// /// Threading: callable by any thread. void fetchAll(bool wait, OpContainer & ops); + + /// Disallow further request queuing + /// + /// Threading: callable by any thread. + void stopQueue(); protected: static HttpRequestQueue * sInstance; @@ -97,6 +103,7 @@ protected: OpContainer mQueue; LLCoreInt::HttpMutex mQueueMutex; LLCoreInt::HttpConditionVariable mQueueCV; + bool mQueueStopped; }; // end class HttpRequestQueue diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 25f64acc42..faafd9a6c7 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -172,6 +172,8 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio void HttpService::shutdown() { + mRequestQueue->stopQueue(); + // *FIXME: Run down everything.... } diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index e906ff8a1e..6d13a213f5 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -154,7 +154,12 @@ HttpHandle HttpRequest::requestGet(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -183,7 +188,12 @@ HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -211,7 +221,12 @@ HttpHandle HttpRequest::requestPost(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -239,7 +254,12 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id, return handle; } op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfers refcount + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -255,7 +275,12 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) HttpOpNull * op = new HttpOpNull(); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -287,14 +312,19 @@ HttpStatus HttpRequest::update(long millis) // Request Management Methods // ==================================== -HttpHandle HttpRequest::requestCancel(HttpHandle handle, HttpHandler * user_handler) +HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler) { HttpStatus status; HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); - HttpOpCancel * op = new HttpOpCancel(handle); + HttpOpCancel * op = new HttpOpCancel(request); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } mLastReqStatus = status; ret_handle = static_cast(op); @@ -311,7 +341,12 @@ HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priori HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); op->setReplyPath(mReplyQueue, handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } mLastReqStatus = status; ret_handle = static_cast(op); @@ -368,7 +403,12 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) HttpOpStop * op = new HttpOpStop(); op->setReplyPath(mReplyQueue, user_handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); @@ -388,7 +428,12 @@ HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandl HttpOpSetGet * op = new HttpOpSetGet(); op->setupSet(GP_HTTP_PROXY, proxy); op->setReplyPath(mReplyQueue, handler); - mRequestQueue->addOp(op); // transfer refcount as well + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } mLastReqStatus = status; handle = static_cast(op); diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index cac927cfca..8e62cd0f92 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -149,7 +149,7 @@ void HttpRequestTestObjectType::test<1>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used", mMemTotal < GetMemTotal()); // release the request object delete req; @@ -158,7 +158,7 @@ void HttpRequestTestObjectType::test<1>() HttpRequest::destroyService(); // make sure we didn't leak any memory - ensure(mMemTotal == GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { @@ -187,11 +187,11 @@ void HttpRequestTestObjectType::test<2>() // create a new ref counted object with an implicit reference req = new HttpRequest(); - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used", mMemTotal < GetMemTotal()); // Issue a NoOp HttpHandle handle = req->requestNoOp(NULL); - ensure(handle != LLCORE_HTTP_HANDLE_INVALID); + ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); // release the request object delete req; @@ -199,15 +199,15 @@ void HttpRequestTestObjectType::test<2>() // We're still holding onto the operation which is // sitting, unserviced, on the request queue so... - ensure(mMemTotal < GetMemTotal()); + ensure("Memory being used 2", mMemTotal < GetMemTotal()); // Request queue should have two references: global singleton & service object ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); // Okay, tear it down HttpRequest::destroyService(); - // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); - ensure(mMemTotal == GetMemTotal()); + printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) { -- cgit v1.2.3 From ed55eec8e352a9c38670a9155bc239748f578da4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 12:44:54 -0400 Subject: Always another platform build fix. Mac this time. --- indra/llcorehttp/tests/test_httprequest.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 8e62cd0f92..b09db6db28 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -206,7 +206,7 @@ void HttpRequestTestObjectType::test<2>() // Okay, tear it down HttpRequest::destroyService(); - printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); ensure("Memory returned", mMemTotal == GetMemTotal()); } catch (...) -- cgit v1.2.3 From 5ff1758b633f1984f601aacbb7920c3c744b87f7 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 14:41:08 -0400 Subject: SH-3177, SH-3180 std::iostream and LLSD serialization for BufferArray objects. Seems to be working correctly. Not certain this is the fastest possible way to provide a std::streambuf interface but it's visually acceptable. --- indra/llcorehttp/bufferstream.h | 59 ++++++++++++++++++++++++++++ indra/llcorehttp/tests/test_bufferstream.hpp | 54 ++++++++++++++++++++++++- indra/newview/lltexturefetch.cpp | 27 ++++++------- indra/newview/lltexturefetch.h | 8 ++++ 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h index 60bda9ff9a..9327a798aa 100644 --- a/indra/llcorehttp/bufferstream.h +++ b/indra/llcorehttp/bufferstream.h @@ -34,13 +34,59 @@ #include "bufferarray.h" +/// @file bufferstream.h +/// +/// std::streambuf and std::iostream adapters for BufferArray +/// objects. +/// +/// BufferArrayStreamBuf inherits std::streambuf and implements +/// an unbuffered interface for streambuf. This may or may not +/// be the most time efficient implementation and it is a little +/// challenging. +/// +/// BufferArrayStream inherits std::iostream and will be the +/// adapter object most callers will be interested in (though +/// it uses BufferArrayStreamBuf internally). Instances allow +/// for the usual streaming operators ('<<', '>>') and serialization +/// methods. +/// +/// Example of LLSD serialization to a BufferArray: +/// +/// BufferArray * ba = new BufferArray; +/// BufferArrayStream bas(ba); +/// LLSDSerialize::toXML(llsd, bas); +/// operationOnBufferArray(ba); +/// ba->release(); +/// ba = NULL; +/// // operationOnBufferArray and bas are each holding +/// // references to the ba instance at this point. +/// + namespace LLCore { +// ===================================================== +// BufferArrayStreamBuf +// ===================================================== + +/// Adapter class to put a std::streambuf interface on a BufferArray +/// +/// Application developers will rarely be interested in anything +/// other than the constructor and even that will rarely be used +/// except indirectly via the @BufferArrayStream class. The +/// choice of interfaces implemented yields a bufferless adapter +/// that doesn't used either the input or output pointer triplets +/// of the more common buffered implementations. This may or may +/// not be faster and that question could stand to be looked at +/// sometime. +/// + class BufferArrayStreamBuf : public std::streambuf { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStreamBuf(BufferArray * array); virtual ~BufferArrayStreamBuf(); @@ -74,9 +120,22 @@ protected: }; // end class BufferArrayStreamBuf +// ===================================================== +// BufferArrayStream +// ===================================================== + +/// Adapter class that supplies streaming operators to BufferArray +/// +/// Provides a streaming adapter to an existing BufferArray +/// instance so that the convenient '<<' and '>>' conversions +/// can be applied to a BufferArray. Very convenient for LLSD +/// serialization and parsing as well. + class BufferArrayStream : public std::iostream { public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. BufferArrayStream(BufferArray * ba); ~BufferArrayStream(); diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp index 45ddb7fd80..831c901b9d 100644 --- a/indra/llcorehttp/tests/test_bufferstream.hpp +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -31,6 +31,8 @@ #include #include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" using namespace LLCore; @@ -173,6 +175,8 @@ void BufferStreamTestObjectType::test<5>() const char * content("This is a string. A fragment."); const size_t c_len(strlen(content)); ba->append(content, c_len); + + // Creat an adapter for the BufferArray BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -223,7 +227,7 @@ void BufferStreamTestObjectType::test<6>() //ba->append(content, strlen(content)); { - // create a new ref counted object with an implicit reference + // Creat an adapter for the BufferArray BufferArrayStream bas(ba); ensure("Memory being used", mMemTotal < GetMemTotal()); @@ -246,6 +250,54 @@ void BufferStreamTestObjectType::test<6>() } +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ + set_test_name("BufferArrayStream with LLSD serialization"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // LLSD + LLSD llsd = LLSD::emptyMap(); + + llsd["int"] = LLSD::Integer(3); + llsd["float"] = LLSD::Real(923289.28992); + llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + + LLSD llsd_map = LLSD::emptyMap(); + llsd_map["int"] = LLSD::Integer(-2889); + llsd_map["float"] = LLSD::Real(2.37829e32); + llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + + llsd["map"] = llsd_map; + + // Serialize it + LLSDSerialize::toXML(llsd, bas); + + std::string str; + bas >> str; + // std::cout << "SERIALIZED LLSD: " << str << std::endl; + ensure("Extracted string has reasonable length", str.size() > 60); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + } // end namespace tut diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4995d9f5ea..6b186811f1 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -58,6 +58,7 @@ #include "httphandler.h" #include "httpresponse.h" #include "bufferarray.h" +#include "bufferstream.h" ////////////////////////////////////////////////////////////////////////////// @@ -2182,6 +2183,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpRequest(NULL), mHttpOptions(NULL), mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), mTotalCacheReadCount(0U), mTotalCacheWriteCount(0U), @@ -2194,6 +2196,8 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mHttpOptions = new LLCore::HttpOptions; mHttpHeaders = new LLCore::HttpHeaders; mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); } LLTextureFetch::~LLTextureFetch() @@ -2219,6 +2223,12 @@ LLTextureFetch::~LLTextureFetch() mHttpHeaders = NULL; } + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + mHttpWaitResource.clear(); delete mHttpRequest; @@ -3501,29 +3511,18 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) if (! mCapsURL.empty()) { - // *FIXME: This mess to get an llsd into a string though - // it's actually no worse than what we currently do... - std::stringstream body; - LLSDSerialize::toXML(merged_llsd, body); - std::string body_str(body.str()); - body.clear(); - - LLCore::HttpHeaders * headers = new LLCore::HttpHeaders; - headers->mHeaders.push_back("Content-Type: application/llsd+xml"); - LLCore::BufferArray * ba = new LLCore::BufferArray; - ba->append(body_str.c_str(), body_str.length()); - body_str.clear(); + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); fetcher->getHttpRequest().requestPost(report_policy_class, report_priority, mCapsURL, ba, NULL, - headers, + fetcher->getMetricsHeaders(), handler); ba->release(); - headers->release(); LLTextureFetch::svMetricsDataBreak = false; } else diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index e17c71113a..4d762a0e05 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -157,6 +157,13 @@ public: // Threads: T* LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } + bool isQAMode() const { return mQAMode; } // ---------------------------------- @@ -322,6 +329,7 @@ private: LLCore::HttpRequest * mHttpRequest; // Ttf LLCore::HttpOptions * mHttpOptions; // Ttf LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf // We use a resource semaphore to keep HTTP requests in // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the -- cgit v1.2.3 From 28ccf4190e83670af2f03015ac16db6b9c76c805 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 22 Jun 2012 17:31:46 -0500 Subject: MAINT-653 Fix for shadow acne at high altitudes --- indra/llrender/llshadermgr.cpp | 2 +- indra/newview/app_settings/settings.xml | 2 +- .../shaders/class2/deferred/alphaF.glsl | 37 ++++++++-------- .../shaders/class2/deferred/alphaNonIndexedF.glsl | 36 ++++++++-------- .../class2/deferred/alphaNonIndexedNoColorF.glsl | 39 +++++++++-------- .../shaders/class2/deferred/sunLightF.glsl | 45 ++++++++++---------- .../shaders/class2/deferred/sunLightSSAOF.glsl | 49 +++++++++++----------- indra/newview/pipeline.cpp | 21 +++++----- 8 files changed, 113 insertions(+), 118 deletions(-) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index bf917d4474..e828b7c4e8 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -643,7 +643,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade text[count++] = strdup("#define textureCube texture\n"); text[count++] = strdup("#define texture2DLod textureLod\n"); text[count++] = strdup("#define shadow2D(a,b) vec2(texture(a,b))\n"); - + if (major_version > 1 || minor_version >= 40) { //GLSL 1.40 replaces texture2DRect et al with texture text[count++] = strdup("#define texture2DRect texture\n"); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b7b040f98b..ea54f4b881 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8068,7 +8068,7 @@ Type F32 Value - 0 + -0.007 RenderShadowOffsetError diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index 08f6ec63fe..99bf03ceca 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; VARYING vec4 vertex_color; VARYING vec2 vary_texcoord0; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform mat4 shadow_matrix[6]; @@ -58,18 +58,21 @@ uniform float shadow_bias; uniform mat4 inv_proj; -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; stc.z += shadow_bias; - - float cs = shadow2DRect(shadowMap, stc.xyz).x; + + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs); + + vec2 off = scl/shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); return shadow/5.0; } @@ -97,8 +100,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25)*w; @@ -109,8 +111,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -121,8 +122,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -133,8 +133,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl index aae6a070e2..4f6a88961b 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl @@ -31,17 +31,16 @@ out vec4 frag_color; #define frag_color gl_FragColor #endif -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -54,6 +53,7 @@ VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; VARYING vec4 vertex_color; +uniform vec2 shadow_res; uniform float shadow_bias; uniform mat4 inv_proj; @@ -71,18 +71,20 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; stc.z += shadow_bias; - + float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; + + vec2 off = scl/shadow_res; - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); return shadow/5.0; } @@ -110,8 +112,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25)*w; @@ -122,8 +123,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -134,8 +134,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -146,8 +145,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl index d6848c8dc1..90cd0f8602 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl @@ -33,17 +33,16 @@ out vec4 frag_color; uniform float minimum_alpha; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -55,6 +54,8 @@ VARYING vec3 vary_position; VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform mat4 inv_proj; @@ -72,18 +73,20 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; stc.z += shadow_bias; - - float cs = shadow2DRect(shadowMap, stc.xyz).x; + + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs); + vec2 off = scl/shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); return shadow/5.0; } @@ -118,8 +121,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25)*w; @@ -130,8 +132,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -142,8 +143,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -154,8 +154,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 8c4ccf9cb3..219ae687d5 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -35,10 +35,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; @@ -55,10 +55,10 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; uniform float shadow_bias; uniform float shadow_offset; @@ -78,29 +78,32 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; - - float cs = shadow2DRect(shadowMap, stc.xyz).x; + stc.z += shadow_bias; + + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs); + + vec2 off = 1.5/shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); return shadow/5.0; //return shadow; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -162,8 +165,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25)*w; @@ -174,8 +176,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -186,7 +187,6 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; @@ -198,7 +198,6 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -237,11 +236,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index 02075a7687..f6b009577a 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; uniform sampler2D noiseMap; @@ -55,10 +55,11 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform float shadow_offset; @@ -139,29 +140,31 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm) return min(ret, 1.0); } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; - - float cs = shadow2DRect(shadowMap, stc.xyz).x; + stc.z += shadow_bias; + + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs); - shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs); + vec2 off = 1.5/shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); return shadow/5.0; //return shadow; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -223,8 +226,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25)*w; @@ -235,8 +237,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -247,8 +248,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -259,8 +259,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -298,11 +297,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index a8dd6cf908..4ad0497954 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -885,7 +885,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) { //allocate 4 sun shadow maps for (U32 i = 0; i < 4; i++) { - if (!mShadow[i].allocate(U32(resX*scale),U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE)) return false; + if (!mShadow[i].allocate(U32(resX*scale),U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false; } } else @@ -7278,7 +7278,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n for (U32 i = 0; i < 4; i++) { - channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_TEXTURE); stop_glerror(); if (channel > -1) { @@ -7288,8 +7288,8 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); stop_glerror(); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); stop_glerror(); } } @@ -7369,13 +7369,13 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n matrix_nondiag, matrix_nondiag, matrix_diag}; shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_SSAO_EFFECT_MAT, 1, GL_FALSE, ssao_effect_mat); - F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); - F32 shadow_bias_error = 1.f + RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + //F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + F32 shadow_bias_error = RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2])/3000.f; shader.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); shader.uniform1f(LLShaderMgr::DEFERRED_NEAR_CLIP, LLViewerCamera::getInstance()->getNear()*2.f); - shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset*shadow_offset_error); - shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias*shadow_bias_error); + shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset); //*shadow_offset_error); + shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias+shadow_bias_error); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_OFFSET, RenderSpotShadowOffset); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_BIAS, RenderSpotShadowBias); @@ -8099,9 +8099,9 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) for (U32 i = 0; i < 4; i++) { - if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE) > -1) + if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i) > -1) { - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } } @@ -8517,6 +8517,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera gGL.setColorMask(false, false); LLFastTimer ftm(FTM_SHADOW_SIMPLE); + gGL.getTexUnit(0)->disable(); for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) { -- cgit v1.2.3 From bc7d5b24d16963a2715e880c518a4706a99f02fa Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 22 Jun 2012 19:13:50 -0400 Subject: This sets down the groundwork for dynamic policy classes. Groundwork is used for the default class which currently represents texture fetching. Class options implemented from API user into HttpLibcurl. Policy layer is going to start doing some traffic shaping like work to solve problems with consumer-grade gear. Need to have dynamic aspects to policies and that starts now... --- indra/llcorehttp/CMakeLists.txt | 2 + indra/llcorehttp/_httplibcurl.cpp | 51 ++++++++------ indra/llcorehttp/_httplibcurl.h | 7 +- indra/llcorehttp/_httppolicy.cpp | 69 ++++++++++++++++--- indra/llcorehttp/_httppolicy.h | 22 +++--- indra/llcorehttp/_httppolicyclass.cpp | 125 ++++++++++++++++++++++++++++++++++ indra/llcorehttp/_httppolicyclass.h | 59 ++++++++++++++++ indra/llcorehttp/_httpservice.cpp | 21 +++++- indra/llcorehttp/_httpservice.h | 28 +++++--- indra/llcorehttp/httprequest.cpp | 29 +++++--- 10 files changed, 351 insertions(+), 62 deletions(-) create mode 100644 indra/llcorehttp/_httppolicyclass.cpp create mode 100644 indra/llcorehttp/_httppolicyclass.h diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 4c00cc04e9..f3df9bb94f 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -37,6 +37,7 @@ set(llcorehttp_SOURCE_FILES _httpopsetget.cpp _httpopsetpriority.cpp _httppolicy.cpp + _httppolicyclass.cpp _httppolicyglobal.cpp _httpreplyqueue.cpp _httprequestqueue.cpp @@ -63,6 +64,7 @@ set(llcorehttp_HEADER_FILES _httpopsetget.h _httpopsetpriority.h _httppolicy.h + _httppolicyclass.h _httppolicyglobal.h _httpreadyqueue.h _httpreplyqueue.h diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 65eb642056..45e16d420e 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -39,17 +39,10 @@ namespace LLCore HttpLibcurl::HttpLibcurl(HttpService * service) - : mService(service) -{ - // *FIXME: Use active policy class count later - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) - { - mMultiHandles[policy_class] = 0; - } - - // Create multi handle for default class - mMultiHandles[0] = curl_multi_init(); -} + : mService(service), + mPolicyCount(0), + mMultiHandles(NULL) +{} HttpLibcurl::~HttpLibcurl() @@ -64,17 +57,23 @@ HttpLibcurl::~HttpLibcurl() mActiveOps.erase(item); } - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + if (mMultiHandles) { - if (mMultiHandles[policy_class]) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { - // *FIXME: Do some multi cleanup here first + if (mMultiHandles[policy_class]) + { + // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); - mMultiHandles[policy_class] = 0; + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } } - } + delete [] mMultiHandles; + mMultiHandles = NULL; + } + mService = NULL; } @@ -87,12 +86,26 @@ void HttpLibcurl::term() {} +void HttpLibcurl::setPolicyCount(int policy_count) +{ + llassert_always(policy_count <= POLICY_CLASS_LIMIT); + llassert_always(! mMultiHandles); // One-time call only + + mPolicyCount = policy_count; + mMultiHandles = new CURLM * [mPolicyCount]; + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + mMultiHandles[policy_class] = curl_multi_init(); + } +} + + HttpService::ELoopSpeed HttpLibcurl::processTransport() { HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); // Give libcurl some cycles to do I/O & callbacks - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mMultiHandles); ++policy_class) + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) { if (! mMultiHandles[policy_class]) continue; @@ -147,7 +160,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() void HttpLibcurl::addOp(HttpOpRequest * op) { - llassert_always(op->mReqPolicy < POLICY_CLASS_LIMIT); + llassert_always(op->mReqPolicy < mPolicyCount); llassert_always(mMultiHandles[op->mReqPolicy] != NULL); // Create standard handle diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 0d0c4cad6d..5e1dd1bfbf 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -78,6 +78,10 @@ public: /// additional references will be added.) void addOp(HttpOpRequest * op); + /// One-time call to set the number of policy classes to be + /// serviced and to create the resources for each. + void setPolicyCount(int policy_count); + int getActiveCount() const; int getActiveCountInClass(int policy_class) const; @@ -92,7 +96,8 @@ protected: protected: HttpService * mService; // Simple reference, not owner active_set_t mActiveOps; - CURLM * mMultiHandles[POLICY_CLASS_LIMIT]; + int mPolicyCount; + CURLM ** mMultiHandles; }; // end class HttpLibcurl } // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4be9f1d45f..1dae20add6 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -31,6 +31,7 @@ #include "_httpoprequest.h" #include "_httpservice.h" #include "_httplibcurl.h" +#include "_httppolicyclass.h" #include "lltimer.h" @@ -38,14 +39,44 @@ namespace LLCore { + +struct HttpPolicy::State +{ +public: + State() + : mConnMax(DEFAULT_CONNECTIONS), + mConnAt(DEFAULT_CONNECTIONS), + mConnMin(2), + mNextSample(0), + mErrorCount(0), + mErrorFactor(0) + {} + + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + + HttpPolicyClass mOptions; + + long mConnMax; + long mConnAt; + long mConnMin; + + HttpTime mNextSample; + unsigned long mErrorCount; + unsigned long mErrorFactor; +}; + + HttpPolicy::HttpPolicy(HttpService * service) - : mService(service) + : mActiveClasses(0), + mState(NULL), + mService(service) {} HttpPolicy::~HttpPolicy() { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); while (! retryq.empty()) @@ -67,13 +98,27 @@ HttpPolicy::~HttpPolicy() readyq.pop(); } } + delete [] mState; + mState = NULL; mService = NULL; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global) +void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, + const std::vector & classes) { + llassert_always(! mState); + mGlobalOptions = global; + mActiveClasses = classes.size(); + mState = new State [mActiveClasses]; + for (int i(0); i < mActiveClasses; ++i) + { + mState[i].mOptions = classes[i]; + mState[i].mConnMax = classes[i].mConnectionLimit; + mState[i].mConnAt = mState[i].mConnMax; + mState[i].mConnMin = 2; + } } @@ -123,13 +168,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); HttpLibcurl & transport(mService->getTransport()); - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { + State & state(mState[policy_class]); int active(transport.getActiveCountInClass(policy_class)); - int needed(DEFAULT_CONNECTIONS - active); // *FIXME: move to policy class + int needed(state.mConnAt - active); // Expect negatives here - HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); - HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + HttpRetryQueue & retryq(state.mRetryQueue); + HttpReadyQueue & readyq(state.mReadyQueue); if (needed > 0) { @@ -174,9 +220,10 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue() bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { - for (int policy_class(0); policy_class < LL_ARRAY_SIZE(mState); ++policy_class) + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { - HttpReadyQueue::container_type & c(mState[policy_class].mReadyQueue.get_container()); + State & state(mState[policy_class]); + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); // Scan ready queue for requests that match policy for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) @@ -188,7 +235,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior HttpOpRequest * op(*cur); c.erase(cur); // All iterators are now invalidated op->mReqPriority = priority; - mState[policy_class].mReadyQueue.push(op); // Re-insert using adapter class + state.mReadyQueue.push(op); // Re-insert using adapter class return true; } } @@ -242,7 +289,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) { - if (policy_class < POLICY_CLASS_LIMIT) // *FIXME: use actual active class count + if (policy_class < mActiveClasses) { return (mState[policy_class].mReadyQueue.size() + mState[policy_class].mRetryQueue.size()); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 05de9303b5..c93279bc83 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -33,6 +33,7 @@ #include "_httpreadyqueue.h" #include "_httpretryqueue.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" #include "_httpinternal.h" @@ -92,26 +93,25 @@ public: // Get pointer to global policy options. Caller is expected // to do context checks like no setting once running. - HttpPolicyGlobal & getGlobalOptions() + HttpPolicyGlobal & getGlobalOptions() { return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global); + void setPolicies(const HttpPolicyGlobal & global, + const std::vector & classes); + // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); protected: - struct State - { - HttpReadyQueue mReadyQueue; - HttpRetryQueue mRetryQueue; - }; - - State mState[POLICY_CLASS_LIMIT]; - HttpService * mService; // Naked pointer, not refcounted, not owner - HttpPolicyGlobal mGlobalOptions; + struct State; + + int mActiveClasses; + State * mState; + HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; }; // end class HttpPolicy diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp new file mode 100644 index 0000000000..8007468d3c --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -0,0 +1,125 @@ +/** + * @file _httppolicyclass.cpp + * @brief Definitions for internal class defining class policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyclass.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyClass::HttpPolicyClass() + : mSetMask(0UL), + mConnectionLimit(DEFAULT_CONNECTIONS), + mPerHostConnectionLimit(DEFAULT_CONNECTIONS), + mPipelining(0) +{} + + +HttpPolicyClass::~HttpPolicyClass() +{} + + +HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mPerHostConnectionLimit = other.mPerHostConnectionLimit; + mPipelining = other.mPipelining; + } + return *this; +} + + +HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) + : mSetMask(other.mSetMask), + mConnectionLimit(other.mConnectionLimit), + mPerHostConnectionLimit(other.mPerHostConnectionLimit), + mPipelining(other.mPipelining) +{} + + +HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), long(LIMIT_CONNECTIONS_MAX)); + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + mPerHostConnectionLimit = llclamp(value, long(LIMIT_CONNECTIONS_MIN), mConnectionLimit); + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + mPipelining = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + src = &mPerHostConnectionLimit; + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + src = &mPipelining; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h new file mode 100644 index 0000000000..d175413cbd --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.h @@ -0,0 +1,59 @@ +/** + * @file _httppolicyclass.h + * @brief Declarations for internal class defining policy class options. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_CLASS_H_ +#define _LLCORE_HTTP_POLICY_CLASS_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyClass +{ +public: + HttpPolicyClass(); + ~HttpPolicyClass(); + + HttpPolicyClass & operator=(const HttpPolicyClass &); + HttpPolicyClass(const HttpPolicyClass &); // Not defined + +public: + HttpStatus set(HttpRequest::EClassPolicy opt, long value); + HttpStatus get(HttpRequest::EClassPolicy opt, long * value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + long mPerHostConnectionLimit; + long mPipelining; +}; // end class HttpPolicyClass + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_CLASS_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index faafd9a6c7..9c5c7bf9b4 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,6 +53,12 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + HttpPolicyClass pol_class; + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); + pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); + + mPolicyClasses.push_back(pol_class); } @@ -114,6 +120,18 @@ void HttpService::term() } +HttpRequest::policy_t HttpService::createPolicyClass() +{ + const HttpRequest::policy_t policy_class(mPolicyClasses.size()); + if (policy_class >= POLICY_CLASS_LIMIT) + { + return 0; + } + mPolicyClasses.push_back(HttpPolicyClass()); + return policy_class; +} + + bool HttpService::isStopped() { // What is really wanted here is something like: @@ -142,7 +160,8 @@ void HttpService::startThread() } // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal); + mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); + mTransport->setPolicyCount(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 3f953ec1a7..43044d97c0 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -28,9 +28,12 @@ #define _LLCORE_HTTP_SERVICE_H_ +#include + #include "httpcommon.h" #include "httprequest.h" #include "_httppolicyglobal.h" +#include "_httppolicyclass.h" namespace LLCoreInt @@ -163,6 +166,14 @@ public: { return mPolicyGlobal; } + + HttpRequest::policy_t createPolicyClass(); + + HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) + { + llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); + return mPolicyClasses[policy_class]; + } protected: void threadRun(LLCoreInt::HttpThread * thread); @@ -170,20 +181,21 @@ protected: ELoopSpeed processRequestQueue(ELoopSpeed loop); protected: - static HttpService * sInstance; + static HttpService * sInstance; // === shared data === - static volatile EState sState; - HttpRequestQueue * mRequestQueue; - volatile bool mExitRequested; + static volatile EState sState; + HttpRequestQueue * mRequestQueue; + volatile bool mExitRequested; // === calling-thread-only data === - LLCoreInt::HttpThread * mThread; - HttpPolicyGlobal mPolicyGlobal; + LLCoreInt::HttpThread * mThread; + HttpPolicyGlobal mPolicyGlobal; + std::vector mPolicyClasses; // === working-thread-only data === - HttpPolicy * mPolicy; // Simple pointer, has ownership - HttpLibcurl * mTransport; // Simple pointer, has ownership + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership }; // end class HttpService } // end namespace LLCore diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 6d13a213f5..a525d8f9ea 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -92,26 +92,31 @@ HttpRequest::~HttpRequest() HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) { - // *FIXME: Fail if thread is running. - + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } return HttpService::instanceOf()->getGlobalOptions().set(opt, value); } HttpRequest::policy_t HttpRequest::createPolicyClass() { - // *FIXME: Implement classes - policy_t policy_id = 1; - - return policy_id; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return 0; + } + return HttpService::instanceOf()->createPolicyClass(); } @@ -119,9 +124,11 @@ HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value) { - HttpStatus status; - - return status; + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); } -- cgit v1.2.3 From e172ec84fa217aae8d1e51c1e0673322c30891fe Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 23 Jun 2012 23:33:50 -0400 Subject: SH-3184/SH-3221 Improve cleanup, destructor, thread termination, etc. logic in library. With this commit, the cleanup paths should be production quality. Unit tests have been expanded to include cases requiring thread termination and cleanup by the worker thread. Special operation/request added to support the unit tests. Thread interface expanded to include a very aggressive cancel() method that does not do cleanup but prevents the thread from accessing objects that will be destroyed. --- indra/llcorehttp/_httplibcurl.cpp | 97 +++++++++++++----- indra/llcorehttp/_httplibcurl.h | 16 +-- indra/llcorehttp/_httpoperation.cpp | 36 +++++++ indra/llcorehttp/_httpoperation.h | 25 +++++ indra/llcorehttp/_httppolicy.cpp | 15 ++- indra/llcorehttp/_httppolicy.h | 15 ++- indra/llcorehttp/_httprequestqueue.h | 6 +- indra/llcorehttp/_httpservice.cpp | 75 ++++++++++---- indra/llcorehttp/_httpservice.h | 11 +- indra/llcorehttp/_thread.h | 20 +++- indra/llcorehttp/httprequest.cpp | 43 ++++++-- indra/llcorehttp/httprequest.h | 9 ++ indra/llcorehttp/tests/test_httprequest.hpp | 154 +++++++++++++++++++++++++++- 13 files changed, 447 insertions(+), 75 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 45e16d420e..bc8b3cc9be 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -47,12 +47,19 @@ HttpLibcurl::HttpLibcurl(HttpService * service) HttpLibcurl::~HttpLibcurl() { - // *FIXME: need to cancel requests in this class, not in op class. + shutdown(); + + mService = NULL; +} + + +void HttpLibcurl::shutdown() +{ while (! mActiveOps.empty()) { active_set_t::iterator item(mActiveOps.begin()); - (*item)->cancel(); + cancelRequest(*item); (*item)->release(); mActiveOps.erase(item); } @@ -63,8 +70,6 @@ HttpLibcurl::~HttpLibcurl() { if (mMultiHandles[policy_class]) { - // *FIXME: Do some multi cleanup here first - curl_multi_cleanup(mMultiHandles[policy_class]); mMultiHandles[policy_class] = 0; } @@ -73,20 +78,12 @@ HttpLibcurl::~HttpLibcurl() delete [] mMultiHandles; mMultiHandles = NULL; } - - mService = NULL; -} - -void HttpLibcurl::init() -{} - - -void HttpLibcurl::term() -{} + mPolicyCount = 0; +} -void HttpLibcurl::setPolicyCount(int policy_count) +void HttpLibcurl::start(int policy_count) { llassert_always(policy_count <= POLICY_CLASS_LIMIT); llassert_always(! mMultiHandles); // One-time call only @@ -143,8 +140,9 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() } else { - // *FIXME: Issue a logging event for this. - ; + LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; } msgs_in_queue = 0; } @@ -191,30 +189,61 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. Caller is expected to +// remove to op from the active list and release the op *after* +// calling this method. It must be called first to deliver the +// op to the reply queue with refcount intact. +void HttpLibcurl::cancelRequest(HttpOpRequest * op) +{ + // Deactivate request + op->mCurlActive = false; + + // Detach from multi and recycle handle + curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + curl_easy_cleanup(op->mCurlHandle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " + << static_cast(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Cancel op and deliver for notification + op->cancel(); +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) { HttpOpRequest * op(NULL); curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer if (handle != op->mCurlHandle || ! op->mCurlActive) { - // *FIXME: This is a sanity check that needs validation/termination. - ; + LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; } active_set_t::iterator it(mActiveOps.find(op)); if (mActiveOps.end() == it) { - // *FIXME: Fatal condition. This must be here. - ; - } - else - { - mActiveOps.erase(it); + LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast(handle) + << LL_ENDL; + return false; } // Deactivate request + mActiveOps.erase(it); op->mCurlActive = false; // Set final status of request if it hasn't failed by other mechanisms yet @@ -258,9 +287,21 @@ int HttpLibcurl::getActiveCount() const } -int HttpLibcurl::getActiveCountInClass(int /* policy_class */) const +int HttpLibcurl::getActiveCountInClass(int policy_class) const { - return getActiveCount(); + int count(0); + + for (active_set_t::const_iterator iter(mActiveOps.begin()); + mActiveOps.end() != iter; + ++iter) + { + if ((*iter)->mReqPolicy == policy_class) + { + ++count; + } + } + + return count; } diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 5e1dd1bfbf..69f7bb2b6d 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -65,9 +65,6 @@ private: void operator=(const HttpLibcurl &); // Not defined public: - static void init(); - static void term(); - /// Give cycles to libcurl to run active requests. Completed /// operations (successful or failed) will be retried or handed /// over to the reply queue as final responses. @@ -79,16 +76,23 @@ public: void addOp(HttpOpRequest * op); /// One-time call to set the number of policy classes to be - /// serviced and to create the resources for each. - void setPolicyCount(int policy_count); + /// serviced and to create the resources for each. Value + /// must agree with HttpPolicy::setPolicies() call. + void start(int policy_count); + + void shutdown(); int getActiveCount() const; int getActiveCountInClass(int policy_class) const; - + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + + /// Invoked to cancel an active request, mainly during shutdown + /// and destroy. + void cancelRequest(HttpOpRequest * op); protected: typedef std::set active_set_t; diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index 0d9553434e..b35ab79d65 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -208,4 +208,40 @@ void HttpOpNull::stageFromRequest(HttpService * service) } +// ================================== +// HttpOpSpin +// ================================== + + +HttpOpSpin::HttpOpSpin(int mode) + : HttpOperation(), + mMode(mode) +{} + + +HttpOpSpin::~HttpOpSpin() +{} + + +void HttpOpSpin::stageFromRequest(HttpService * service) +{ + if (0 == mMode) + { + // Spin forever + while (true) + { + ms_sleep(100); + } + } + else + { + this->addRef(); + if (! HttpRequestQueue::instanceOf()->addOp(this)) + { + this->release(); + } + } +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h index 5823c08c7b..717a9b0d72 100644 --- a/indra/llcorehttp/_httpoperation.h +++ b/indra/llcorehttp/_httpoperation.h @@ -170,6 +170,31 @@ public: }; // end class HttpOpNull +/// HttpOpSpin is a test-only request that puts the worker +/// thread into a cpu spin. Used for unit tests and cleanup +/// evaluation. You do not want to use this. +class HttpOpSpin : public HttpOperation +{ +public: + // 0 does a hard spin in the operation + // 1 does a soft spin continuously requeuing itself + HttpOpSpin(int mode); + +protected: + virtual ~HttpOpSpin(); + +private: + HttpOpSpin(const HttpOpSpin &); // Not defined + void operator=(const HttpOpSpin &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + int mMode; +}; // end class HttpOpSpin + + } // end namespace LLCore #endif // _LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1dae20add6..93e295537c 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -75,6 +75,14 @@ HttpPolicy::HttpPolicy(HttpService * service) HttpPolicy::~HttpPolicy() +{ + shutdown(); + + mService = NULL; +} + + +void HttpPolicy::shutdown() { for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { @@ -100,12 +108,12 @@ HttpPolicy::~HttpPolicy() } delete [] mState; mState = NULL; - mService = NULL; + mActiveClasses = 0; } -void HttpPolicy::setPolicies(const HttpPolicyGlobal & global, - const std::vector & classes) +void HttpPolicy::start(const HttpPolicyGlobal & global, + const std::vector & classes) { llassert_always(! mState); @@ -244,6 +252,7 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior return false; } + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index c93279bc83..90bb3b571d 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -60,6 +60,17 @@ private: void operator=(const HttpPolicy &); // Not defined public: + /// Cancel all ready and retry requests sending them to + /// their notification queues. Release state resources + /// making further request handling impossible. + void shutdown(); + + /// Deliver policy definitions and enable handling of + /// requests. One-time call invoked before starting + /// the worker thread. + void start(const HttpPolicyGlobal & global, + const std::vector & classes); + /// Give the policy layer some cycles to scan the ready /// queue promoting higher-priority requests to active /// as permited. @@ -98,10 +109,6 @@ public: return mGlobalOptions; } - void setPolicies(const HttpPolicyGlobal & global, - const std::vector & classes); - - // Get ready counts for a particular class int getReadyCount(HttpRequest::policy_t policy_class); diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h index 6e8f00c4da..e11fd17c90 100644 --- a/indra/llcorehttp/_httprequestqueue.h +++ b/indra/llcorehttp/_httprequestqueue.h @@ -73,11 +73,15 @@ public: public: typedef std::vector OpContainer; - /// Insert an object at the back of the reply queue. + /// Insert an object at the back of the request queue. /// /// Caller must provide one refcount to the queue which takes /// possession of the count. /// + /// @return Standard status. On failure, caller + /// must dispose of the operation with + /// an explicit release() call. + /// /// Threading: callable by any thread. HttpStatus addOp(HttpOperation * op); diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 9c5c7bf9b4..afbab2ab71 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -53,35 +53,43 @@ HttpService::HttpService() mPolicy(NULL), mTransport(NULL) { + // Create the default policy class HttpPolicyClass pol_class; pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, DEFAULT_CONNECTIONS); pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); - mPolicyClasses.push_back(pol_class); } HttpService::~HttpService() { + mExitRequested = true; + if (RUNNING == sState) + { + // Trying to kill the service object with a running thread + // is a bit tricky. + if (mThread) + { + mThread->cancel(); + + if (! mThread->timedJoin(2000)) + { + // Failed to join, expect problems ahead... + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; + } + } + } + if (mRequestQueue) { mRequestQueue->release(); mRequestQueue = NULL; } - if (mPolicy) - { - // *TODO: need a finalization here - ; - } - - if (mTransport) - { - // *TODO: need a finalization here - delete mTransport; - mTransport = NULL; - } + delete mTransport; + mTransport = NULL; delete mPolicy; mPolicy = NULL; @@ -110,9 +118,22 @@ void HttpService::init(HttpRequestQueue * queue) void HttpService::term() { - llassert_always(RUNNING != sState); if (sInstance) { + if (RUNNING == sState) + { + // Unclean termination. Thread appears to be running. We'll + // try to give the worker thread a chance to cancel using the + // exit flag... + sInstance->mExitRequested = true; + + // And a little sleep + ms_sleep(1000); + + // Dtor will make some additional efforts and issue any final + // warnings... + } + delete sInstance; sInstance = NULL; } @@ -159,9 +180,9 @@ void HttpService::startThread() mThread->release(); } - // Push current policy definitions - mPolicy->setPolicies(mPolicyGlobal, mPolicyClasses); - mTransport->setPolicyCount(mPolicyClasses.size()); + // Push current policy definitions, enable policy & transport components + mPolicy->start(mPolicyGlobal, mPolicyClasses); + mTransport->start(mPolicyClasses.size()); mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); mThread->addRef(); // Need an explicit reference, implicit one is used internally @@ -174,6 +195,7 @@ void HttpService::stopRequested() mExitRequested = true; } + bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) { bool found(false); @@ -191,9 +213,26 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio void HttpService::shutdown() { + // Disallow future enqueue of requests mRequestQueue->stopQueue(); - // *FIXME: Run down everything.... + // Cancel requests alread on the request queue + HttpRequestQueue::OpContainer ops; + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + op->cancel(); + op->release(); + } + + // Shutdown transport canceling requests, freeing resources + mTransport->shutdown(); + + // And now policy + mPolicy->shutdown(); } diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index 43044d97c0..a74235c475 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -134,7 +134,7 @@ public: /// acquires its weaknesses. static bool isStopped(); - /// Threading: callable by application thread *once*. + /// Threading: callable by consumer thread *once*. void startThread(); /// Threading: callable by worker thread. @@ -152,23 +152,28 @@ public: /// Threading: callable by worker thread. bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + /// Threading: callable by worker thread. HttpPolicy & getPolicy() { return *mPolicy; } + /// Threading: callable by worker thread. HttpLibcurl & getTransport() { return *mTransport; } + /// Threading: callable by consumer thread. HttpPolicyGlobal & getGlobalOptions() { return mPolicyGlobal; } + /// Threading: callable by consumer thread. HttpRequest::policy_t createPolicyClass(); + /// Threading: callable by consumer thread. HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) { llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); @@ -187,9 +192,9 @@ protected: static volatile EState sState; HttpRequestQueue * mRequestQueue; volatile bool mExitRequested; - - // === calling-thread-only data === LLCoreInt::HttpThread * mThread; + + // === consumer-thread-only data === HttpPolicyGlobal mPolicyGlobal; std::vector mPolicyClasses; diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h index 46a333a749..4cf35055e9 100644 --- a/indra/llcorehttp/_thread.h +++ b/indra/llcorehttp/_thread.h @@ -27,9 +27,11 @@ #ifndef LLCOREINT_THREAD_H_ #define LLCOREINT_THREAD_H_ +#include "linden_common.h" + #include #include - +#include #include "_refcounted.h" @@ -91,11 +93,27 @@ public: mThread->join(); } + inline bool timedJoin(S32 millis) + { + return mThread->timed_join(boost::posix_time::milliseconds(millis)); + } + inline bool joinable() const { return mThread->joinable(); } + // A very hostile method to force a thread to quit + inline void cancel() + { + boost::thread::native_handle_type thread(mThread->native_handle()); +#if LL_WINDOWS + TerminateThread(thread, 0); +#else + pthread_cancel(thread); +#endif + } + private: boost::function mThreadFunc; boost::thread * mThread; diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index a525d8f9ea..3a55a849b9 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -370,11 +370,13 @@ HttpStatus HttpRequest::createService() { HttpStatus status; - llassert_always(! has_inited); - HttpRequestQueue::init(); - HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); - HttpService::init(rq); - has_inited = true; + if (! has_inited) + { + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + } return status; } @@ -384,10 +386,12 @@ HttpStatus HttpRequest::destroyService() { HttpStatus status; - llassert_always(has_inited); - HttpService::term(); - HttpRequestQueue::term(); - has_inited = false; + if (has_inited) + { + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + } return status; } @@ -423,6 +427,27 @@ HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) return handle; } + +HttpHandle HttpRequest::requestSpin(int mode) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSpin * op = new HttpOpSpin(mode); + op->setReplyPath(mReplyQueue, NULL); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast(op); + + return handle; +} + // ==================================== // Dynamic Policy Methods // ==================================== diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 24fff24b83..134a61b618 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -409,6 +409,15 @@ public: /// HttpHandle requestStopThread(HttpHandler * handler); + /// Queue a Spin request. + /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for + /// test purposes. + /// + /// @param mode 0 for hard spin, 1 for soft spin + /// @return Standard handle return cases. + /// + HttpHandle requestSpin(int mode); + /// @} /// @name DynamicPolicyMethods diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index b09db6db28..ed049aa09c 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1230,10 +1230,158 @@ void HttpRequestTestObjectType::test<11>() } } +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + // *NB: This test must be last. The sleeping webserver // won't respond for a long time. template <> template <> -void HttpRequestTestObjectType::test<12>() +void HttpRequestTestObjectType::test<14>() { ScopedCurlInit ready; @@ -1352,7 +1500,9 @@ void HttpRequestTestObjectType::test<12>() throw; } } - +// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a +// sleeping service that interferes with other HTTP tests. Keep it +// last until that little HTTP server can get some attention... } // end namespace tut -- cgit v1.2.3 From e35f3b7db4803c287e95fb5aaa563e8432e71349 Mon Sep 17 00:00:00 2001 From: MartinRJ Fayray Date: Mon, 25 Jun 2012 05:09:22 +0200 Subject: STORM-1879: Inventory gear menu remains on screen after floater is closed --- indra/llui/llfloater.cpp | 4 ++++ indra/llui/lltoggleablemenu.cpp | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 8ca1e685a9..90251ac7c6 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -748,6 +748,10 @@ void LLFloater::closeFloater(bool app_quitting) dependee->setFocus(TRUE); } } + + // STORM-1879: since this floater has focus, treat the closeFloater- call + // like a click on the close-button, and close gear- and contextmenus + LLMenuGL::sMenuContainer->hideMenus(); } dirtyRect(); diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp index d29260750f..e4d1a37569 100644 --- a/indra/llui/lltoggleablemenu.cpp +++ b/indra/llui/lltoggleablemenu.cpp @@ -57,7 +57,9 @@ void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn) S32 x,y; LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y); - if (!curVisibilityIn && mButtonRect.pointInRect(x, y)) + // STORM-1879: also check MouseCapture to see if the button was really + // clicked (otherwise the VisibilityChange was triggered via keyboard shortcut) + if (!curVisibilityIn && mButtonRect.pointInRect(x, y) && gFocusMgr.getMouseCapture()) { mClosedByButtonClick = true; } -- cgit v1.2.3 From 0ee53b21d2a7bf2cd37c7bcdc2a86e0f079fbefc Mon Sep 17 00:00:00 2001 From: Kelly Washington Date: Mon, 25 Jun 2012 13:41:53 -0700 Subject: MAINT-30 Estate access lists for groups and residents are blank when "Allow public access" is checked. MAINT-1200 Resident names not sorted in About Land (or Estate management) floater's allowed/banned boxes reviewed with Simon --- indra/llui/llscrolllistctrl.cpp | 5 ++ indra/llui/llscrolllistctrl.h | 1 + indra/newview/llfloaterland.cpp | 23 +++--- indra/newview/llfloaterregioninfo.cpp | 147 +++++++++------------------------- indra/newview/llfloaterregioninfo.h | 4 - indra/newview/llnamelistctrl.cpp | 8 +- indra/newview/llnamelistctrl.h | 2 + 7 files changed, 64 insertions(+), 126 deletions(-) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index b3e1b63db5..82a914a30a 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -2704,6 +2704,11 @@ BOOL LLScrollListCtrl::hasSortOrder() const return !mSortColumns.empty(); } +void LLScrollListCtrl::clearSortOrder() +{ + mSortColumns.clear(); +} + void LLScrollListCtrl::clearColumns() { column_map_t::iterator itor; diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index ae8aea9245..6660c07b8e 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -373,6 +373,7 @@ public: std::string getSortColumnName(); BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } BOOL hasSortOrder() const; + void clearSortOrder(); S32 selectMultiple( uuid_vec_t ids ); // conceptually const, but mutates mItemList diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index ee18c95b34..5515e93d0e 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2371,12 +2371,6 @@ LLPanelLandAccess::~LLPanelLandAccess() void LLPanelLandAccess::refresh() { LLFloater* parent_floater = gFloaterView->getParentFloater(this); - - if (mListAccess) - mListAccess->deleteAllItems(); - if (mListBanned) - mListBanned->deleteAllItems(); - LLParcel *parcel = mParcel->getParcel(); // Display options @@ -2394,7 +2388,11 @@ void LLPanelLandAccess::refresh() getChild("GroupCheck")->setLabelArg("[GROUP]", group_name ); // Allow list + if (mListAccess) { + // Clear the sort order so we don't re-sort on every add. + mListAccess->clearSortOrder(); + mListAccess->deleteAllItems(); S32 count = parcel->mAccessList.size(); getChild("AccessList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); getChild("AccessList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST)); @@ -2429,13 +2427,17 @@ void LLPanelLandAccess::refresh() } suffix.append(" " + parent_floater->getString("Remaining") + ")"); } - if (mListAccess) - mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); + mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListAccess->sortByName(TRUE); } // Ban List + if(mListBanned) { + // Clear the sort order so we don't re-sort on every add. + mListBanned->clearSortOrder(); + mListBanned->deleteAllItems(); S32 count = parcel->mBanList.size(); getChild("BannedList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); @@ -2473,6 +2475,7 @@ void LLPanelLandAccess::refresh() } mListBanned->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListBanned->sortByName(TRUE); } if(parcel->getRegionDenyAnonymousOverride()) @@ -2608,13 +2611,13 @@ void LLPanelLandAccess::refresh_ui() getChildView("AccessList")->setEnabled(can_manage_allowed); S32 allowed_list_count = parcel->mAccessList.size(); getChildView("add_allowed")->setEnabled(can_manage_allowed && allowed_list_count < PARCEL_MAX_ACCESS_LIST); - BOOL has_selected = mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0; + BOOL has_selected = (mListAccess && mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_allowed")->setEnabled(can_manage_allowed && has_selected); getChildView("BannedList")->setEnabled(can_manage_banned); S32 banned_list_count = parcel->mBanList.size(); getChildView("add_banned")->setEnabled(can_manage_banned && banned_list_count < PARCEL_MAX_ACCESS_LIST); - has_selected = mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0; + has_selected = (mListBanned && mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_banned")->setEnabled(can_manage_banned && has_selected); } } diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 17850ff35d..fe29bb38c7 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -287,8 +287,7 @@ void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**) //dispatch the message dispatch.dispatch(request, invoice, strings); - LLViewerRegion* region = gAgent.getRegion(); - panel->updateControls(region); + panel->updateControls(gAgent.getRegion()); } @@ -1924,10 +1923,18 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) BOOL manager = (region && region->isEstateManager()); setCtrlsEnabled(god || owner || manager); + BOOL has_allowed_avatar = getChild("allowed_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_allowed_group = getChild("allowed_group_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_banned_agent = getChild("banned_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_estate_manager = getChild("estate_manager_name_list")->getFirstSelected() ? TRUE : FALSE; + getChildView("add_allowed_avatar_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_avatar_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_avatar_btn")->setEnabled(has_allowed_avatar && (god || owner || manager)); + getChildView("allowed_avatar_name_list")->setEnabled(god || owner || manager); + getChildView("add_allowed_group_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_group_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_group_btn")->setEnabled(has_allowed_group && (god || owner || manager) ); + getChildView("allowed_group_name_list")->setEnabled(god || owner || manager); // Can't ban people from mainland, orientation islands, etc. because this // creates much network traffic and server load. @@ -1935,14 +1942,15 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) bool linden_estate = isLindenEstate(); bool enable_ban = (god || owner || manager) && !linden_estate; getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); + getChildView("remove_banned_avatar_btn")->setEnabled(has_banned_agent && enable_ban); + getChildView("banned_avatar_name_list")->setEnabled(god || owner || manager); getChildView("message_estate_btn")->setEnabled(god || owner || manager); getChildView("kick_user_from_estate_btn")->setEnabled(god || owner || manager); // estate managers can't add estate managers getChildView("add_estate_manager_btn")->setEnabled(god || owner); - getChildView("remove_estate_manager_btn")->setEnabled(god || owner); + getChildView("remove_estate_manager_btn")->setEnabled(has_estate_manager && (god || owner)); getChildView("estate_manager_name_list")->setEnabled(god || owner); refresh(); @@ -1979,10 +1987,8 @@ bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) void LLPanelEstateInfo::updateChild(LLUICtrl* child_ctrl) { - if (checkRemovalButton(child_ctrl->getName())) - { - // do nothing - } + // Ensure appropriate state of the management ui. + updateControls(gAgent.getRegion()); } bool LLPanelEstateInfo::estateUpdate(LLMessageSystem* msg) @@ -2080,23 +2086,8 @@ void LLPanelEstateInfo::refreshFromEstate() getChild("limit_payment")->setValue(estate_info.getDenyAnonymous()); getChild("limit_age_verified")->setValue(estate_info.getDenyAgeUnverified()); - // If visible from mainland, disable the access allowed - // UI, as anyone can teleport there. - // However, gods need to be able to edit the access list for - // linden estates, regardless of visibility, to allow object - // and L$ transfers. - { - bool visible_from_mainland = estate_info.getIsExternallyVisible(); - bool god = gAgent.isGodlike(); - bool linden_estate = isLindenEstate(); - - bool enable_agent = (!visible_from_mainland || (god && linden_estate)); - bool enable_group = enable_agent; - bool enable_ban = !linden_estate; - - setAccessAllowedEnabled(enable_agent, enable_group, enable_ban); - } - + // Ensure appriopriate state of the management UI + updateControls(gAgent.getRegion()); refresh(); } @@ -2225,47 +2216,6 @@ void LLPanelEstateInfo::setOwnerName(const std::string& name) getChild("estate_owner")->setValue(LLSD(name)); } -void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent, - bool enable_group, - bool enable_ban) -{ - getChildView("allow_resident_label")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setVisible( enable_agent); - getChildView("add_allowed_avatar_btn")->setEnabled(enable_agent); - getChildView("remove_allowed_avatar_btn")->setEnabled(enable_agent); - - // Groups - getChildView("allow_group_label")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setVisible( enable_group); - getChildView("add_allowed_group_btn")->setEnabled(enable_group); - getChildView("remove_allowed_group_btn")->setEnabled(enable_group); - - // Ban - getChildView("ban_resident_label")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setVisible( enable_ban); - getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); - - // Update removal buttons if needed - if (enable_agent) - { - checkRemovalButton("allowed_avatar_name_list"); - } - - if (enable_group) - { - checkRemovalButton("allowed_group_name_list"); - } - - if (enable_ban) - { - checkRemovalButton("banned_avatar_name_list"); - } -} - void LLPanelEstateInfo::clearAccessLists() { LLNameListCtrl* name_list = getChild("allowed_avatar_name_list"); @@ -2279,39 +2229,7 @@ void LLPanelEstateInfo::clearAccessLists() { name_list->deleteAllItems(); } -} - -// enables/disables the "remove" button for the various allow/ban lists -BOOL LLPanelEstateInfo::checkRemovalButton(std::string name) -{ - std::string btn_name = ""; - if (name == "allowed_avatar_name_list") - { - btn_name = "remove_allowed_avatar_btn"; - } - else if (name == "allowed_group_name_list") - { - btn_name = "remove_allowed_group_btn"; - } - else if (name == "banned_avatar_name_list") - { - btn_name = "remove_banned_avatar_btn"; - } - else if (name == "estate_manager_name_list") - { - //ONLY OWNER CAN ADD /DELET ESTATE MANAGER - LLViewerRegion* region = gAgent.getRegion(); - if (region && (region->getOwner() == gAgent.getID())) - { - btn_name = "remove_estate_manager_btn"; - } - } - - // enable the remove button if something is selected - LLNameListCtrl* name_list = getChild(name); - getChildView(btn_name)->setEnabled(name_list && name_list->getFirstSelected() ? TRUE : FALSE); - - return (btn_name != ""); + updateControls(gAgent.getRegion()); } // static @@ -2792,15 +2710,15 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_agent_name_list) { - //allowed_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + allowed_agent_name_list->clearSortOrder(); for (S32 i = 0; i < num_allowed_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_agent_name_list->addNameItem(id); } - panel->getChildView("remove_allowed_avatar_btn")->setEnabled(allowed_agent_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_agent_name_list->sortByColumnIndex(0, TRUE); + allowed_agent_name_list->sortByName(TRUE); } } @@ -2817,6 +2735,8 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_group_name_list) { + // Don't sort these as we add them, sort them when we are done. + allowed_group_name_list->clearSortOrder(); allowed_group_name_list->deleteAllItems(); for (S32 i = 0; i < num_allowed_groups && i < ESTATE_MAX_GROUP_IDS; i++) { @@ -2824,8 +2744,7 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_group_name_list->addGroupNameItem(id); } - panel->getChildView("remove_allowed_group_btn")->setEnabled(allowed_group_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_group_name_list->sortByColumnIndex(0, TRUE); + allowed_group_name_list->sortByName(TRUE); } } @@ -2849,15 +2768,16 @@ bool LLDispatchSetEstateAccess::operator()( if (banned_agent_name_list) { - //banned_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + banned_agent_name_list->clearSortOrder(); + for (S32 i = 0; i < num_banned_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ banned_agent_name_list->addNameItem(id); } - panel->getChildView("remove_banned_avatar_btn")->setEnabled(banned_agent_name_list->getFirstSelected() ? TRUE : FALSE); - banned_agent_name_list->sortByColumnIndex(0, TRUE); + banned_agent_name_list->sortByName(TRUE); } } @@ -2872,6 +2792,9 @@ bool LLDispatchSetEstateAccess::operator()( panel->getChild("estate_manager_name_list"); if (estate_manager_name_list) { + // Don't sort these as we add them, sort them when we are done. + estate_manager_name_list->clearSortOrder(); + estate_manager_name_list->deleteAllItems(); // Clear existing entries // There should be only ESTATE_MAX_MANAGERS people in the list, but if the database gets more (SL-46107) don't @@ -2883,11 +2806,13 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ estate_manager_name_list->addNameItem(id); } - panel->getChildView("remove_estate_manager_btn")->setEnabled(estate_manager_name_list->getFirstSelected() ? TRUE : FALSE); - estate_manager_name_list->sortByColumnIndex(0, TRUE); + estate_manager_name_list->sortByName(TRUE); } } + // Update the buttons which may change based on the list contents but also needs to account for general access features. + panel->updateControls(gAgent.getRegion()); + return true; } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index e36ef4604b..f0499f1903 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -312,9 +312,6 @@ public: const std::string getOwnerName() const; void setOwnerName(const std::string& name); - // If visible from mainland, allowed agent and allowed groups - // are ignored, so must disable UI. - void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban); protected: virtual BOOL sendUpdate(); // confirmation dialog callback @@ -324,7 +321,6 @@ protected: void commitEstateManagers(); void clearAccessLists(); - BOOL checkRemovalButton(std::string name); BOOL checkSunHourSlider(LLUICtrl* child_ctrl); U32 mEstateID; diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 4e28d1f526..11b057eb0d 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -401,7 +401,7 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, name = av_name.getCompleteName(); item_list::iterator iter; - for (iter = getItemList().begin(); iter != getItemList().end(); iter++) + for (iter = getItemList().begin(); iter != getItemList().end(); ++iter) { LLScrollListItem* item = *iter; if (item->getUUID() == agent_id) @@ -410,6 +410,7 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, if (cell) { cell->setValue(name); + setNeedsSort(); } } } @@ -431,3 +432,8 @@ void LLNameListCtrl::updateColumns() } } } + +void LLNameListCtrl::sortByName(BOOL ascending) +{ + sortByColumnIndex(mNameColumnIndex,ascending); +} diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index ca9956dc53..77c21f92e2 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -110,6 +110,8 @@ public: void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; } + void sortByName(BOOL ascending); + /*virtual*/ void updateColumns(); /*virtual*/ void mouseOverHighlightNthItem( S32 index ); -- cgit v1.2.3 From 3ca7e94dbe735318929203b1d5c781f7a04acf0a Mon Sep 17 00:00:00 2001 From: Kelly Washington Date: Mon, 25 Jun 2012 13:50:29 -0700 Subject: MAINT-1169 Delay Restart causes region restart to be cancelled * fixed the label to be 'cancel restart' instead of delay restart. --- indra/newview/skins/default/xui/en/panel_region_debug.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/panel_region_debug.xml b/indra/newview/skins/default/xui/en/panel_region_debug.xml index 4550603134..a4883c21e2 100644 --- a/indra/newview/skins/default/xui/en/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/en/panel_region_debug.xml @@ -194,7 +194,7 @@ + + Parcel: + + + - - - - - - - - - - + + + + 1, Total number of fetched textures: [NUM] + + + 2, Total number of fetching requests: [NUM] + + + 3, Total number of cache hits: [NUM] + + + 4, Total number of visible textures: [NUM] + + + 5, Total number of visible texture fetching requests: [NUM] + + + 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB + + + 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 9, Total time on cache readings: [TIME] seconds + + + 10, Total time on cache writings: [TIME] seconds + + + 11, Total time on decodings: [TIME] seconds + + + 12, Total time on gl texture creation: [TIME] seconds + + + 13, Total time on HTTP fetching: [TIME] seconds + + + 14, Total time on entire fetching: [TIME] seconds + + + 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + + + + + + + + + + + + + -- cgit v1.2.3 From f199b298b4bbd01702ea248236d00a1e05b7bdeb Mon Sep 17 00:00:00 2001 From: "simon@Simon-PC.lindenlab.com" Date: Mon, 2 Jul 2012 16:40:18 -0700 Subject: MAINT-971 : [PUBLIC]Renaming a new inventory item while filtering item names renames parent folder instead. Reviewed by Kelly --- indra/newview/llinventoryfunctions.cpp | 50 +++++++++++++++++++++------------- indra/newview/llinventoryfunctions.h | 1 + indra/newview/llinventorypanel.cpp | 1 + indra/newview/llpanelmaininventory.cpp | 1 + 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ab5b082915..e98d3f88a6 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -440,7 +440,7 @@ void show_item_original(const LLUUID& item_uuid) //sidetray inventory panel LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - bool reset_inventory_filter = !floater_inventory->isInVisibleChain(); + bool do_reset_inventory_filter = !floater_inventory->isInVisibleChain(); LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); if (!active_panel) @@ -460,37 +460,49 @@ void show_item_original(const LLUUID& item_uuid) } active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); - if(reset_inventory_filter) + if(do_reset_inventory_filter) { - //inventory floater - bool floater_inventory_visible = false; + reset_inventory_filter(); + } +} + - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) +void reset_inventory_filter() +{ + //inventory floater + bool floater_inventory_visible = false; + + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterInventory* floater_inventory = dynamic_cast(*iter); + if (floater_inventory) { - LLFloaterInventory* floater_inventory = dynamic_cast(*iter); - if (floater_inventory) - { - LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); + LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); - main_inventory->onFilterEdit(""); + main_inventory->onFilterEdit(""); - if(floater_inventory->getVisible()) - { - floater_inventory_visible = true; - } + if(floater_inventory->getVisible()) + { + floater_inventory_visible = true; } } - if(sidepanel_inventory && !floater_inventory_visible) + } + + if(!floater_inventory_visible) + { + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) { LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); - - main_inventory->onFilterEdit(""); + if (main_inventory) + { + main_inventory->onFilterEdit(""); + } } } } - void open_outbox() { LLFloaterReg::showInstance("outbox"); diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 5cf9c528b0..909f7fd10b 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -56,6 +56,7 @@ void show_item_profile(const LLUUID& item_uuid); void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id); void show_item_original(const LLUUID& item_uuid); +void reset_inventory_filter(); void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 71dd963f28..05c81957c6 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -966,6 +966,7 @@ void LLInventoryPanel::doToSelected(const LLSD& userdata) void LLInventoryPanel::doCreate(const LLSD& userdata) { + reset_inventory_filter(); menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata); } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c11597f532..9f3273da2d 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -306,6 +306,7 @@ void LLPanelMainInventory::newWindow() void LLPanelMainInventory::doCreate(const LLSD& userdata) { + reset_inventory_filter(); menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); } -- cgit v1.2.3 From 2d7b7de20327a40be12a620debaae9917af16cd6 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 3 Jul 2012 13:06:46 -0400 Subject: More integration work for texture fetch timeouts. The fetch state machine received a new timeout during the WAIT_HTTP_REQ state. For the integration, rather than jump the state to done, we issue a request cancel and let the notification plumbing do the rest without any race conditions or special-case logic. --- indra/llcorehttp/_httplibcurl.cpp | 24 +++++++++++++++++++ indra/llcorehttp/_httplibcurl.h | 3 +++ indra/llcorehttp/_httpopcancel.cpp | 6 ++++- indra/llcorehttp/_httppolicy.cpp | 49 ++++++++++++++++++++++++++++++++++++-- indra/llcorehttp/_httppolicy.h | 3 +++ indra/llcorehttp/_httpservice.cpp | 25 +++++++++++++++++++ indra/llcorehttp/_httpservice.h | 8 +++++++ indra/newview/lltexturefetch.cpp | 20 ++++++++++++++-- 8 files changed, 133 insertions(+), 5 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 39abca12c5..3c69ae1c96 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -189,6 +189,30 @@ void HttpLibcurl::addOp(HttpOpRequest * op) } +// Implements the transport part of any cancel operation. +// See if the handle is an active operation and if so, +// use the more complicated transport-based cancelation +// method to kill the request. +bool HttpLibcurl::cancel(HttpHandle handle) +{ + HttpOpRequest * op(static_cast(handle)); + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + return false; + } + + // Cancel request + cancelRequest(op); + + // Drop references + mActiveOps.erase(it); + op->release(); + + return true; +} + + // *NOTE: cancelRequest logic parallels completeRequest logic. // Keep them synchronized as necessary. Caller is expected to // remove to op from the active list and release the op *after* diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h index 69f7bb2b6d..53972b1ffa 100644 --- a/indra/llcorehttp/_httplibcurl.h +++ b/indra/llcorehttp/_httplibcurl.h @@ -85,6 +85,9 @@ public: int getActiveCount() const; int getActiveCountInClass(int policy_class) const; + // Shadows HttpService's method + bool cancel(HttpHandle handle); + protected: /// Invoked when libcurl has indicated a request has been processed /// to completion and we need to move the request to a new state. diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index ad624d2e57..5c1f484109 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -61,7 +61,11 @@ HttpOpCancel::~HttpOpCancel() void HttpOpCancel::stageFromRequest(HttpService * service) { - // *FIXME: Need cancel functionality into services + if (! service->cancel(mHandle)) + { + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + addAsReply(); } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 4350ff617b..1b10805b72 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -231,9 +231,12 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) { State & state(mState[policy_class]); - HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); - + // 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++); @@ -253,6 +256,48 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior } +bool HttpPolicy::cancel(HttpHandle handle) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + + // Scan retry queue + HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container()); + for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;) + { + HttpRetryQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c1.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + + // Scan ready queue + HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast(*cur) == handle) + { + HttpOpRequest * op(*cur); + c2.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + } + + return false; +} + bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) { static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h index 90bb3b571d..a02bf084c1 100644 --- a/indra/llcorehttp/_httppolicy.h +++ b/indra/llcorehttp/_httppolicy.h @@ -92,6 +92,9 @@ public: // Shadows HttpService's method bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + // Shadows HttpService's method as well + bool cancel(HttpHandle handle); + /// When transport is finished with an op and takes it off the /// active queue, it is delivered here for dispatch. Policy /// may send it back to the ready/retry queues if it needs another diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp index 92c15b5b8f..f7d9813db0 100644 --- a/indra/llcorehttp/_httpservice.cpp +++ b/indra/llcorehttp/_httpservice.cpp @@ -219,6 +219,31 @@ bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t prio } + /// Try to find the given request handle on any of the request + /// queues and cancel the operation. + /// + /// @return True if the request was canceled. + /// + /// Threading: callable by worker thread. +bool HttpService::cancel(HttpHandle handle) +{ + bool canceled(false); + + // Request can't be on request queue so skip that. + + // Check the policy component's queues first + canceled = mPolicy->cancel(handle); + + if (! canceled) + { + // If that didn't work, check transport's. + canceled = mTransport->cancel(handle); + } + + return canceled; +} + + /// Threading: callable by worker thread. void HttpService::shutdown() { diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h index d67e6e95a5..d24c497ca9 100644 --- a/indra/llcorehttp/_httpservice.h +++ b/indra/llcorehttp/_httpservice.h @@ -154,6 +154,14 @@ public: /// 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. + /// + /// @return True if the request was found and canceled. + /// + /// Threading: callable by worker thread. + bool cancel(HttpHandle handle); + /// Threading: callable by worker thread. HttpPolicy & getPolicy() { diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 36b878d6f2..b30b25e543 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1560,8 +1560,24 @@ bool LLTextureFetchWorker::doWork(S32 param) if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) { //timeout, abort. - mState = DONE; - return true; + LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after " + << mRequestedTimer.getElapsedTimeF32() + << " seconds. Canceling request." << LL_ENDL; + + if (LLCORE_HTTP_HANDLE_INVALID != mHttpHandle) + { + // Issue cancel on any outstanding request. Asynchronous + // so cancel may not actually take effect if operation is + // complete & queued. Either way, notification will + // complete and the request can be transitioned. + mFetcher->mHttpRequest->requestCancel(mHttpHandle, NULL); + } + else + { + // Shouldn't happen but if it does, cancel quickly. + mState = DONE; + return true; + } } setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -- cgit v1.2.3 From d1c056beba8c3558756eac816e26a388a512e0ee Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 3 Jul 2012 14:35:08 -0500 Subject: MAINT-685 Fix for disabling basic shaders making high detail terrain render incorrectly. --- indra/llrender/llglstates.h | 5 ----- indra/llrender/llrender.cpp | 13 ++++++++++++- indra/llrender/llrender.h | 1 + indra/newview/lldrawpool.cpp | 1 + indra/newview/lldrawpoolterrain.cpp | 6 +++++- indra/newview/lldrawpooltree.cpp | 1 + indra/newview/llvosurfacepatch.cpp | 4 ++-- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/indra/llrender/llglstates.h b/indra/llrender/llglstates.h index e26aead676..0e2c3bcb44 100644 --- a/indra/llrender/llglstates.h +++ b/indra/llrender/llglstates.h @@ -59,7 +59,6 @@ protected: LLGLEnable mColorMaterial; LLGLDisable mAlphaTest, mBlend, mCullFace, mDither, mFog, mLineSmooth, mLineStipple, mNormalize, mPolygonSmooth, - mTextureGenQ, mTextureGenR, mTextureGenS, mTextureGenT, mGLMultisample; public: LLGLSDefault() @@ -76,10 +75,6 @@ public: mLineStipple(GL_LINE_STIPPLE), mNormalize(GL_NORMALIZE), mPolygonSmooth(GL_POLYGON_SMOOTH), - mTextureGenQ(GL_TEXTURE_GEN_Q), - mTextureGenR(GL_TEXTURE_GEN_R), - mTextureGenS(GL_TEXTURE_GEN_S), - mTextureGenT(GL_TEXTURE_GEN_T), mGLMultisample(GL_MULTISAMPLE_ARB) { } }; diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 348c1eb1b7..4597d06260 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -648,7 +648,7 @@ void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eT gGL.flush(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); } - + // We want an early out, because this function does a LOT of stuff. if ( ( (isAlpha && (mCurrAlphaOp == op) && (mCurrAlphaSrc1 == src1) && (mCurrAlphaSrc2 == src2)) || (!isAlpha && (mCurrColorOp == op) && (mCurrColorSrc1 == src1) && (mCurrColorSrc2 == src2)) ) && !gGL.mDirty) @@ -1437,6 +1437,17 @@ void LLRender::matrixMode(U32 mode) mMatrixMode = mode; } +U32 LLRender::getMatrixMode() +{ + if (mMatrixMode >= MM_TEXTURE0 && mMatrixMode <= MM_TEXTURE3) + { //always return MM_TEXTURE if current matrix mode points at any texture matrix + return MM_TEXTURE; + } + + return mMatrixMode; +} + + void LLRender::loadIdentity() { flush(); diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index fa5f7f311d..78a310e525 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -346,6 +346,7 @@ public: void loadIdentity(); void multMatrix(const GLfloat* m); void matrixMode(U32 mode); + U32 getMatrixMode(); const glh::matrix4f& getModelviewMatrix(); const glh::matrix4f& getProjectionMatrix(); diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 6c0be0a5c2..b8c143e9c1 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -418,6 +418,7 @@ void LLRenderPass::applyModelMatrix(LLDrawInfo& params) gGL.loadMatrix(gGLModelView); if (params.mModelMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) params.mModelMatrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 7fc78fb382..9bc32fddbd 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -308,6 +308,7 @@ void LLDrawPoolTerrain::drawLoop() if (model_matrix != gGLLastMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGLLastMatrix = model_matrix; gGL.loadMatrix(gGLModelView); if (model_matrix) @@ -594,7 +595,8 @@ void LLDrawPoolTerrain::renderFull4TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); - + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Set alpha texture and do lighting modulation gGL.getTexUnit(3)->setTextureColorBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_PREV_COLOR, LLTexUnit::TBS_VERT_COLOR); gGL.getTexUnit(3)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA); @@ -742,6 +744,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); @@ -781,6 +784,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-2.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 83f04e45a8..fedbd782dc 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -116,6 +116,7 @@ void LLDrawPoolTree::render(S32 pass) gGL.loadMatrix(gGLModelView); if (model_matrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) model_matrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 94a3111f4c..cb905d02da 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -80,9 +80,9 @@ public: glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL])); } if (data_mask & MAP_TEXCOORD3) - { //substitute tex coord 0 for tex coord 3 + { //substitute tex coord 1 for tex coord 3 glClientActiveTextureARB(GL_TEXTURE3_ARB); - glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0])); + glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1])); glClientActiveTextureARB(GL_TEXTURE0_ARB); } if (data_mask & MAP_TEXCOORD2) -- cgit v1.2.3 From e38a676c087d0adce9bd35cf3bdf8ff0e898f201 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 3 Jul 2012 19:22:24 -0400 Subject: Add CURLE_SEND_ERROR and CURLE_RECV_ERROR to the set of retryable errors. Data problems after connections are established should be retried as well. Extend to appropriate libcurl codes. Also allow our connectivity to drop to as low as a single connection when trying to recover. --- indra/llcorehttp/_httppolicy.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index 1b10805b72..1e64924198 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -46,7 +46,7 @@ public: State() : mConnMax(DEFAULT_CONNECTIONS), mConnAt(DEFAULT_CONNECTIONS), - mConnMin(2), + mConnMin(1), mNextSample(0), mErrorCount(0), mErrorFactor(0) @@ -303,6 +303,8 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); + static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); // Retry or finalize if (! op->mStatus) @@ -313,7 +315,9 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || cant_connect == op->mStatus || cant_res_proxy == op->mStatus || - cant_res_host == op->mStatus)) + cant_res_host == op->mStatus || + send_error == op->mStatus || + recv_error == op->mStatus)) { // Okay, worth a retry. We include 499 in this test as // it's the old 'who knows?' error from many grid services... -- cgit v1.2.3 From 3a56fbf867194ec8fbc7d8237f9781ac9d72c8a4 Mon Sep 17 00:00:00 2001 From: Kelly Washington Date: Tue, 3 Jul 2012 23:34:56 +0000 Subject: MAINT-1231 Investigate increasing MAX_CACHED_GROUPS in llgroupmgr.cpp * increased max groups to 20 * changed cache to LRU instead of 'delete half when overfull' reviewed with Baker --- indra/newview/llgroupmgr.cpp | 55 +++++++++++++++++++++++++++++++++----------- indra/newview/llgroupmgr.h | 6 ++++- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index efffd0f98e..aceb7f0614 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -63,7 +63,7 @@ #pragma warning(pop) // Restore all warnings to the previous state #endif -const U32 MAX_CACHED_GROUPS = 10; +const U32 MAX_CACHED_GROUPS = 20; // // LLRoleActionSet @@ -234,10 +234,16 @@ LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) : mRoleDataComplete(FALSE), mRoleMemberDataComplete(FALSE), mGroupPropertiesDataComplete(FALSE), - mPendingRoleMemberRequest(FALSE) + mPendingRoleMemberRequest(FALSE), + mAccessTime(0.0f) { } +void LLGroupMgrGroupData::setAccessed() +{ + mAccessTime = (F32)LLFrameTimer::getTotalSeconds(); +} + BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data) { role_data_map_t::const_iterator it; @@ -1360,7 +1366,7 @@ void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data) LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) { - LLGroupMgrGroupData* group_datap; + LLGroupMgrGroupData* group_datap = NULL; group_map_t::iterator existing_group = LLGroupMgr::getInstance()->mGroups.find(id); if (existing_group == LLGroupMgr::getInstance()->mGroups.end()) @@ -1373,6 +1379,11 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) group_datap = existing_group->second; } + if (group_datap) + { + group_datap->setAccessed(); + } + return group_datap; } @@ -1413,25 +1424,41 @@ void LLGroupMgr::notifyObservers(LLGroupChange gc) void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap) { - if (mGroups.size() > MAX_CACHED_GROUPS) + while (mGroups.size() >= MAX_CACHED_GROUPS) { - // get rid of groups that aren't observed - for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; ) + // LRU: Remove the oldest un-observed group from cache until group size is small enough + + F32 oldest_access = LLFrameTimer::getTotalSeconds(); + group_map_t::iterator oldest_gi = mGroups.end(); + + for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end(); ++gi ) { observer_multimap_t::iterator oi = mObservers.find(gi->first); if (oi == mObservers.end()) { - // not observed - LLGroupMgrGroupData* unobserved_groupp = gi->second; - delete unobserved_groupp; - mGroups.erase(gi++); - } - else - { - ++gi; + if (gi->second + && (gi->second->getAccessTime() < oldest_access)) + { + oldest_access = gi->second->getAccessTime(); + oldest_gi = gi; + } } } + + if (oldest_gi != mGroups.end()) + { + delete oldest_gi->second; + mGroups.erase(oldest_gi); + } + else + { + // All groups must be currently open, none to remove. + // Just add the new group anyway, but get out of this loop as it + // will never drop below max_cached_groups. + break; + } } + mGroups[group_datap->getID()] = group_datap; } diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index faf0531c10..df3cd17e03 100644 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -86,7 +86,7 @@ public: BOOL isInRole(const LLUUID& role_id) { return (mRolesList.find(role_id) != mRolesList.end()); } -protected: +private: LLUUID mID; S32 mContribution; U64 mAgentPowers; @@ -233,6 +233,9 @@ public: BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; } BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; } + F32 getAccessTime() const { return mAccessTime; } + void setAccessed(); + public: typedef std::map member_list_t; typedef std::map role_list_t; @@ -280,6 +283,7 @@ private: BOOL mGroupPropertiesDataComplete; BOOL mPendingRoleMemberRequest; + F32 mAccessTime; }; struct LLRoleAction -- cgit v1.2.3 From adce38800a3ac428c3e0b89fe5d62ca3baf97471 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 4 Jul 2012 23:30:58 -0400 Subject: Example program needs to set Accept: header to talk to Caps router. --- indra/llcorehttp/examples/http_texture_load.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index 7efdf53959..e5951e8415 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -39,6 +39,7 @@ #include "httprequest.h" #include "httphandler.h" #include "httpresponse.h" +#include "httpheaders.h" #include "bufferarray.h" #include "_mutex.h" @@ -76,6 +77,7 @@ class WorkingSet : public LLCore::HttpHandler { public: WorkingSet(); + ~WorkingSet(); bool reload(LLCore::HttpRequest *); @@ -111,6 +113,7 @@ public: int mErrorsHttp503; int mSuccesses; long mByteCount; + LLCore::HttpHeaders * mHeaders; }; @@ -316,6 +319,19 @@ WorkingSet::WorkingSet() mByteCount(0L) { mTextures.reserve(30000); + + mHeaders = new LLCore::HttpHeaders; + mHeaders->mHeaders.push_back("Accept: image/x-j2c"); +} + + +WorkingSet::~WorkingSet() +{ + if (mHeaders) + { + mHeaders->release(); + mHeaders = NULL; + } } @@ -337,11 +353,11 @@ bool WorkingSet::reload(LLCore::HttpRequest * hr) LLCore::HttpHandle handle; if (offset || length) { - handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, NULL, this); + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this); } else { - handle = hr->requestGet(0, 0, buffer, NULL, NULL, this); + handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this); } if (! handle) { -- cgit v1.2.3 From 266209b6a3b938e8762dd6fe2c25e87e120cf127 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 6 Jul 2012 14:44:50 -0600 Subject: trivial: convert to Unix line endings. --- indra/newview/llface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index f4e46ae3ea..ced6f4b1aa 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -2033,12 +2033,12 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) F32 dist = lookAt.getLength3().getF32(); dist = llmax(dist-size.getLength3().getF32(), 0.001f); - //ramp down distance for nearby objects - if (dist < 16.f) - { - dist /= 16.f; - dist *= dist; - dist *= 16.f; + //ramp down distance for nearby objects + if (dist < 16.f) + { + dist /= 16.f; + dist *= dist; + dist *= 16.f; } lookAt.normalize3fast() ; -- cgit v1.2.3 From e40d9081c6e0a69a5718d1e3ba9c7bcbffc39304 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 6 Jul 2012 14:57:35 -0600 Subject: fix for SH-3234: Map tile fetching broken in drano build --- indra/newview/lltexturefetch.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5a9cf4aeb6..d6eae7acc5 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1777,7 +1777,10 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, // Clear the url since we're done with the fetch // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch // next time the texture is requested, even if the data have already been fetched. - mUrl.clear(); + if(mWriteToCacheState != NOT_WRITE) + { + mUrl.clear(); + } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -- cgit v1.2.3 From f37b90df5046fbe50309beada01022e35c5aa424 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 18:09:17 -0400 Subject: SH-3222 Slow loading textures on Lag Me 1 Think I have found the major factor that causes the Linksys WRT54G V5 to fall over in testing scenarios: DNS. For some historical reason, we're trying to use libcurl without any DNS caching. My implementation echoed that and implemented it correctly and I was seeing a DNS request per request on the wire. The existing implementation tries to do that and has bugs because it is clearing caching DNS data querying only once every few seconds. Once I started emulating the bug, comms through the WRT became much, much more reliable. --- indra/llcorehttp/_httpinternal.h | 3 +++ indra/llcorehttp/_httpoprequest.cpp | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index bc0bd6a2ab..4ccace2b30 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -77,6 +77,9 @@ const int LOOP_SLEEP_NORMAL_MS = 2; // Block allocation size (a tuning parameter) is found // in bufferarray.h. +// Compatibility controls +const bool ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; + } // end namespace LLCore #endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index 86ecee5b26..a19b6bd0dc 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -385,8 +385,15 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); - // *FIXME: Revisit this old DNS timeout setting - may no longer be valid - curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + if (ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) + { + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); + } + else + { + // *FIXME: Revisit this old DNS timeout setting - may no longer be valid + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + } curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later -- cgit v1.2.3 From d99e867284f4d908e9ee86544726c66719e11e3f Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Fri, 6 Jul 2012 16:36:12 -0600 Subject: fix for MAINT-1201: 404 errors appear in the log when opening the world map --- indra/newview/lltexturefetch.cpp | 6 ++++++ indra/newview/llviewertexture.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 7e6dfbc9d9..2ed7488b85 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1249,6 +1249,12 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 max_attempts; if (mGetStatus == HTTP_NOT_FOUND) { + if(mWriteToCacheState == NOT_WRITE) //map tiles + { + mState = DONE; + return true; // failed, means no map tile on the empty region. + } + mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 7f638a24bf..e1eb54bd24 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -2182,7 +2182,8 @@ void LLViewerFetchedTexture::setIsMissingAsset() } else { - llwarns << mUrl << ": Marking image as missing" << llendl; + //it is normal no map tile on an empty region. + //llwarns << mUrl << ": Marking image as missing" << llendl; } if (mHasFetcher) { -- cgit v1.2.3 From d2af82aafc9ef569c9552f269ccf4e4fd38a1f33 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 19:14:42 -0400 Subject: Experiment with ignoring priority in the library. Let upper layers sort things out or use policy classes (eventually) to arrange low and high priority traffic. Subjectively, I think this works better in practice (as I haven't implemented a dynamic priority setter yet). --- indra/llcorehttp/_httpinternal.h | 8 ++++++++ indra/llcorehttp/_httpreadyqueue.h | 39 +++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h index 4ccace2b30..5f966500c9 100644 --- a/indra/llcorehttp/_httpinternal.h +++ b/indra/llcorehttp/_httpinternal.h @@ -32,6 +32,14 @@ // something wrong is probably happening. +// If '1', internal ready queues will not order ready +// requests by priority, instead it's first-come-first-served. +// Reprioritization requests have the side-effect of then +// putting the modified request at the back of the ready queue. + +#define LLCORE_READY_QUEUE_IGNORES_PRIORITY 1 + + namespace LLCore { diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 87828834dc..968ca01258 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -30,6 +30,7 @@ #include +#include "_httpinternal.h" #include "_httpoprequest.h" @@ -47,10 +48,18 @@ namespace LLCore /// Threading: not thread-safe. Expected to be used entirely by /// a single thread, typically a worker thread of some sort. +#if LLCORE_READY_QUEUE_IGNORES_PRIORITY + +typedef std::deque HttpReadyQueueBase; + +#else + typedef std::priority_queue, LLCore::HttpOpRequestCompare> HttpReadyQueueBase; +#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY + class HttpReadyQueue : public HttpReadyQueueBase { public: @@ -66,16 +75,40 @@ protected: void operator=(const HttpReadyQueue &); // Not defined public: + +#if LLCORE_READY_QUEUE_IGNORES_PRIORITY + // Types and methods needed to make a std::deque look + // more like a std::priority_queue, at least for our + // purposes. + typedef HttpReadyQueueBase container_type; + + const_reference & top() const + { + return front(); + } + + void pop() + { + pop_front(); + } + + void push(const value_type & v) + { + push_back(v); + } + +#endif // LLCORE_READY_QUEUE_IGNORES_PRIORITY + const container_type & get_container() const { - return c; + return *this; } container_type & get_container() { - return c; + return *this; } - + }; // end class HttpReadyQueue -- cgit v1.2.3 From 70e976d1a87f4225c7593129a9c1c4732e2d38e4 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 6 Jul 2012 19:17:23 -0400 Subject: Odd that this was accepted by VS2010. It clearly wasn't right. --- indra/llcorehttp/_httpreadyqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h index 968ca01258..8462b174b5 100644 --- a/indra/llcorehttp/_httpreadyqueue.h +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -82,7 +82,7 @@ public: // purposes. typedef HttpReadyQueueBase container_type; - const_reference & top() const + const_reference top() const { return front(); } -- cgit v1.2.3 From f5f51d3cda8861b3b3a380cc96aaca98e572c377 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 7 Jul 2012 19:35:32 -0400 Subject: SH-3185 Fill in some FIXME/TODO cases Also added some comments and changed the callback userdata argument to be an HttpOpRequest rather than a libcurl handle. Less code, less clutter. --- indra/llcorehttp/_httpopcancel.cpp | 5 +++ indra/llcorehttp/_httpopcancel.h | 7 ++-- indra/llcorehttp/_httpoperation.cpp | 12 +++---- indra/llcorehttp/_httpoprequest.cpp | 62 ++++++++++++++++++++--------------- indra/llcorehttp/_httpoprequest.h | 12 +++++++ indra/llcorehttp/_httpopsetpriority.h | 3 ++ indra/llcorehttp/httpcommon.h | 18 ++++++++++ 7 files changed, 84 insertions(+), 35 deletions(-) diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp index 5c1f484109..8e1105dc81 100644 --- a/indra/llcorehttp/_httpopcancel.cpp +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -59,6 +59,11 @@ HttpOpCancel::~HttpOpCancel() {} +// Immediately search for the request on various queues +// and cancel operations if found. Return the status of +// the search and cancel as the status of this request. +// The canceled request will return a canceled status to +// its handler. void HttpOpCancel::stageFromRequest(HttpService * service) { if (! service->cancel(mHandle)) diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h index 4d927d1aaf..659d28955f 100644 --- a/indra/llcorehttp/_httpopcancel.h +++ b/indra/llcorehttp/_httpopcancel.h @@ -43,9 +43,10 @@ namespace LLCore /// HttpOpCancel requests that a previously issued request -/// be canceled, if possible. Requests that have been made -/// active and are available for sending on the wire cannot -/// be canceled. +/// be canceled, if possible. This includes active requests +/// that may be in the middle of an HTTP transaction. Any +/// completed request will not be canceled and will return +/// its final status unchanged. class HttpOpCancel : public HttpOperation { diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp index d80a8236e6..910dbf1f2f 100644 --- a/indra/llcorehttp/_httpoperation.cpp +++ b/indra/llcorehttp/_httpoperation.cpp @@ -91,31 +91,31 @@ void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, void HttpOperation::stageFromRequest(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromRequest method may not be called." + << LL_ENDL; } void HttpOperation::stageFromReady(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromReady method may not be called." + << LL_ENDL; } void HttpOperation::stageFromActive(HttpService *) { - // *FIXME: Message this a better way later. // Default implementation should never be called. This // indicates an operation making a transition that isn't // defined. - llassert_always(false); + LL_ERRS("HttpCore") << "Default stateFromActive method may not be called." + << LL_ENDL; } diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index a19b6bd0dc..bec82d8449 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -344,6 +344,13 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, } +// Sets all libcurl options and data for a request. +// +// Used both for initial requests and to 'reload' for +// a retry, generally with a different CURL handle. +// Junk may be left around from a failed request and that +// needs to be cleaned out. +// HttpStatus HttpOpRequest::prepareRequest(HttpService * service) { // Scrub transport and result data for retried op case @@ -387,20 +394,29 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) { + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 10); } else { - // *FIXME: Revisit this old DNS timeout setting - may no longer be valid + // *TODO: Revisit this old DNS timeout setting - may no longer be valid + // I don't think this is valid anymore, the Multi shared DNS + // cache is working well. For the case of naked easy handles, + // consider using a shared DNS object. curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); } curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); // *FIXME: parameterize this later + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, DEFAULT_HTTP_REDIRECTS); curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); @@ -468,7 +484,9 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) break; default: - // *FIXME: fail out here + LL_ERRS("CoreHttp") << "Invalid HTTP method in request: " + << int(mReqMethod) << ". Can't recover." + << LL_ENDL; break; } @@ -476,7 +494,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (mTracing >= TRACE_CURL_HEADERS) { curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); - curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this); curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); } @@ -524,7 +542,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); - curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, mCurlHandle); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); } if (status) @@ -537,10 +555,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) { - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); if (! op->mReplyBody) { @@ -554,10 +569,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) { - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); if (! op->mReqBody) { @@ -567,7 +579,8 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void const size_t body_size(op->mReqBody->size()); if (body_size <= op->mCurlBodyPos) { - // *FIXME: should probably log this event - unexplained + LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request." + << LL_ENDL; return 0; } @@ -586,10 +599,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range:"; static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; - CURL * handle(static_cast(userdata)); - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); const size_t hdr_size(size * nmemb); const char * hdr_data(static_cast(data)); // Not null terminated @@ -609,7 +619,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { - char hdr_buffer[128]; + char hdr_buffer[128]; // Enough for a reasonable header size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); memcpy(hdr_buffer, hdr_data, frag_size); @@ -638,8 +648,10 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi else { // Ignore the unparsable. - // *FIXME: Maybe issue a warning into the log here - ; + LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '" + << std::string(hdr_data, frag_size) + << "'. Ignoring." + << LL_ENDL; } } } @@ -668,9 +680,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) { - HttpOpRequest * op(NULL); - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); - // *FIXME: check the pointer + HttpOpRequest * op(static_cast(userdata)); std::string safe_line; std::string tag; diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index 5d2417466c..a4c5fbb3c2 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -49,6 +49,16 @@ class HttpOptions; /// HttpOpRequest requests a supported HTTP method invocation with /// option and header overrides. +/// +/// Essentially an RPC to get an HTTP GET, POST or PUT executed +/// asynchronously with options to override behaviors and HTTP +/// headers. +/// +/// Constructor creates a raw object incapable of useful work. +/// A subsequent call to one of the setupXXX() methods provides +/// the information needed to make a working request which can +/// then be enqueued to a request queue. +/// class HttpOpRequest : public HttpOperation { @@ -177,6 +187,8 @@ public: // Free functions // --------------------------------------- +// Internal function to append the contents of an HttpHeaders +// instance to a curl_slist object. curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); } // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h index 724293ef78..31706b737c 100644 --- a/indra/llcorehttp/_httpopsetpriority.h +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -42,6 +42,9 @@ namespace LLCore /// searches the various queues looking for a given /// request handle and changing it's priority if /// found. +/// +/// *NOTE: This will very likely be removed in the near future +/// when priority is removed from the library. class HttpOpSetPriority : public HttpOperation { diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h index 42b75edb41..576a113e54 100644 --- a/indra/llcorehttp/httpcommon.h +++ b/indra/llcorehttp/httpcommon.h @@ -168,6 +168,24 @@ enum HttpError /// a successful status or an error. The application is responsible /// for making that determination and a range like [200, 299] isn't /// automatically assumed to be definitive. +/// +/// Examples: +/// +/// 1. Construct a default, successful status code: +/// HttpStatus(); +/// +/// 2. Construct a successful, HTTP 200 status code: +/// HttpStatus(200); +/// +/// 3. Construct a failed, HTTP 404 not-found status code: +/// HttpStatus(404); +/// +/// 4. Construct a failed libcurl couldn't connect status code: +/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +/// +/// 5. Construct an HTTP 301 status code to be treated as success: +/// HttpStatus(301, HE_SUCCESS); +/// struct HttpStatus { -- cgit v1.2.3 From 348db20b92f1f1f85712c5a9a862ef079102bbee Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 11:47:47 -0400 Subject: SH-3187 Issue smarter 'Range' requests for textures. First, try to issue ranged GETs that are always at least partially satisfiable. This will keep Varnish-type caches from simply sending back 200/full asset responses to unsatisfiable requests. Implement awareness of Content-Range headers as well. Currently they're not coming back but they will be someday. --- indra/llcorehttp/httpresponse.h | 6 +++ indra/newview/lltexturefetch.cpp | 90 +++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 925cf81586..65e403cec8 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -111,6 +111,12 @@ public: /// If a 'Range:' header was used, these methods are involved /// in setting and returning data about the actual response. + /// If both @offset and @length are returned as 0, we probably + /// didn't get a Content-Range header in the response. This + /// occurs with various Capabilities-based services and the + /// caller is going to have to make assumptions on receipt of + /// a 206 status. The @full value may also be zero in cases of + /// parsing problems or a wild-carded length response. void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const { *offset = mReplyOffset; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index b30b25e543..214a6099d0 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -557,14 +557,14 @@ private: LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data int mHttpPolicyClass; bool mHttpActive; // Active request to http library - unsigned int mHttpReplySize; - unsigned int mHttpReplyOffset; + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore // State history - U32 mCacheReadCount; - U32 mCacheWriteCount; - U32 mResourceWaitCount; + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -1043,6 +1043,8 @@ void LLTextureFetchWorker::resetFormattedData() { mFormattedImage->deleteData(); } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; } @@ -1120,6 +1122,8 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpBufferArray->release(); mHttpBufferArray = NULL; } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); @@ -1402,6 +1406,21 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mRequestedSize -= cur_size; mRequestedOffset = cur_size; + if (mRequestedOffset) + { + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; + } + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; if (!mUrl.empty()) { @@ -1507,9 +1526,29 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; } - const S32 append_size(mHttpBufferArray->size()); - const S32 total_size(cur_size + append_size); + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if (mHttpReplyOffset > cur_size) + { + LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + mState = DONE; + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } if (mFormattedImage.isNull()) { @@ -1522,7 +1561,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData /* && mRequestedDiscard == 0*/) //the image file is fully loaded. + if (mHaveAllData) //the image file is fully loaded. { mFileSize = total_size; } @@ -1536,7 +1575,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - mHttpBufferArray->read(0, (char *) buffer + cur_size, append_size); + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); // NOTE: setData releases current data and owns new data (buffer) mFormattedImage->setData(buffer, total_size); @@ -1544,6 +1583,8 @@ bool LLTextureFetchWorker::doWork(S32 param) // Done with buffer array mHttpBufferArray->release(); mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; mLoadedDiscard = mRequestedDiscard; mState = DECODE_IMAGE; @@ -1576,6 +1617,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // Shouldn't happen but if it does, cancel quickly. mState = DONE; + releaseHttpSemaphore(); return true; } } @@ -2014,15 +2056,33 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - // *FIXME: deal with actual offset and actual datasize, don't assume - // server gave exactly what was asked for. - - llassert_always(NULL == mHttpBufferArray); // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); body->addRef(); mHttpBufferArray = body; + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = mRequestedSize; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + if (! partial) { // Response indicates this is the entire asset regardless @@ -2040,10 +2100,8 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had } - else if (data_size < mRequestedSize /*&& mRequestedDiscard == 0*/) + else if (data_size < mRequestedSize) { - // *FIXME: I think we can treat this as complete regardless - // of requested discard level. Revisit this... mHaveAllData = TRUE; } else if (data_size > mRequestedSize) -- cgit v1.2.3 From 398d78a77348fb4c5ee4e96b7ed6f981b1042627 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 13:28:50 -0400 Subject: Rework the 'sleep' logic in the test HTTP server so that the 30-second hang doesn't break subsequent tests. Did this by introducing threads into the HTTP server as I can't find the magic to detect that my client has gone away. --- indra/llcorehttp/tests/test_httprequest.hpp | 5 ----- indra/llcorehttp/tests/test_llcorehttp_peer.py | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ed4e239fe7..914f35ec3d 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -1381,8 +1381,6 @@ void HttpRequestTestObjectType::test<13>() } -// *NB: This test must be last. The sleeping webserver -// won't respond for a long time. template <> template <> void HttpRequestTestObjectType::test<14>() { @@ -1503,9 +1501,6 @@ void HttpRequestTestObjectType::test<14>() throw; } } -// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a -// sleeping service that interferes with other HTTP tests. Keep it -// last until that little HTTP server can get some attention... } // end namespace tut diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 0e38e5a87f..9e1847c66e 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -32,10 +32,12 @@ $/LicenseInfo$ import os import sys import time +import select from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ThreadingMixIn -mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ +mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/ sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) from indra.util.fastest_elementtree import parse as xml_parse from indra.base import llsd @@ -144,7 +146,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): # Suppress error output as well pass -class Server(HTTPServer): +class Server(ThreadingMixIn, HTTPServer): # This pernicious flag is on by default in HTTPServer. But proper # operation of freeport() absolutely depends on it being off. allow_reuse_address = False -- cgit v1.2.3 From b3659f2eba2a0a43f48e2c395f1a327dc1114098 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 17:04:07 -0400 Subject: Safe implementation of the HTTP resource waiter release method. Doesn't use sets or maps and so there's no ordering assumption to be violated when priorities are changed. Should also be faster. Still want to get rid of the ancillary list, however... --- indra/newview/lltexturefetch.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 214a6099d0..9d3e7eb2b6 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -28,6 +28,7 @@ #include #include +#include #include "llstl.h" @@ -3358,32 +3359,35 @@ void LLTextureFetch::releaseHttpWaiters() if (mHttpWaitResource.empty()) return; - - const size_t limit(mHttpWaitResource.size()); - tids.reserve(limit); - for (wait_http_res_queue_t::iterator iter(mHttpWaitResource.begin()); - mHttpWaitResource.end() != iter; - ++iter) - { - tids.push_back(*iter); - } + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); } // -Mfnq // Now lookup the UUUIDs to find valid requests and sort - // them in priority order, highest to lowest. - typedef std::set worker_set_t; - worker_set_t tids2; - - for (uuid_vec_t::const_iterator iter(tids.begin()); + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); tids.end() != iter; ++iter) { LLTextureFetchWorker * worker(getWorker(* iter)); if (worker) { - tids2.insert(worker); + tids2.push_back(worker); } } + + // Sort into priority order + LLTextureFetchWorker::Compare compare; + std::sort(tids2.begin(), tids2.end(), compare); tids.clear(); // Release workers up to the high water mark. Since we aren't @@ -3391,7 +3395,7 @@ void LLTextureFetch::releaseHttpWaiters() // with other callers. Do defensive things like getting // refreshed counts of requests and checking if someone else // has moved any worker state around.... - for (worker_set_t::iterator iter2(tids2.begin()); + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2 && mHttpSemaphore > 0; ++iter2) { -- cgit v1.2.3 From d6cbe006d3a9448b8897b42c3a817f914a279f02 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 9 Jul 2012 17:29:21 -0400 Subject: Take body size as the reply size when Content-Range header isn't available. --- indra/newview/lltexturefetch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 9d3e7eb2b6..a1f99eeb25 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2074,7 +2074,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, // This could be because it was badly formatted but is more // likely due to capabilities services which scrub headers // from responses. Assume we got what we asked for... - mHttpReplySize = mRequestedSize; + mHttpReplySize = data_size; mHttpReplyOffset = mRequestedOffset; } else -- cgit v1.2.3 From 1c3af2658e30b672d658e1d208764a7e50cb7e5b Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 13:29:24 -0400 Subject: SH-3192 Fix Boost 1.48.0 libraries to support Debug builds on Windows. Tweaked the boost source as per Boost issue #6185 using 1.49.0 sources and this picks up the new build. Debug viewer builds and runs. --- autobuild.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 8a9a01ca61..da4a50638d 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ archive hash - 0c4678ac85395f5f5294b63da1d79007 + 36aa500e13cdde61607b6e93065206ec url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Darwin/installer/boost-1.48.0-darwin-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Darwin/installer/boost-1.48.0-darwin-20120710.tar.bz2 name darwin @@ -198,9 +198,9 @@ archive hash - 848766eac189e0fa785f4a025532acd9 + 18602d44bd435eb0d7189f436ff2cb0f url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/Linux/installer/boost-1.48.0-linux-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Linux/installer/boost-1.48.0-linux-20120710.tar.bz2 name linux @@ -210,9 +210,9 @@ archive hash - cd1e60a00d40f4475ae5e0aca86f74c1 + dc8f5dc6be04c64bf3460b4932b18457 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/249117/arch/CYGWIN/installer/boost-1.48.0-windows-20120208.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/CYGWIN/installer/boost-1.48.0-windows-20120710.tar.bz2 name windows -- cgit v1.2.3 From b6ffedb03da628d809124da24b7f2c20252710a5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Jul 2012 15:46:27 -0400 Subject: MAINT-1175: Reimplement LLTypeInfoLookup for better lookup failure. The original LLTypeInfoLookup implementation was based on two assumptions: small overall container size, and infrequent normal-case lookup failures. Those assumptions led to binary-searching a sorted vector, with linear search as a fallback to cover the problem case of two different type_info* values for the same type. As documented in the Jira, this turned out to be a problem. The container size was larger than expected, and failed lookups turned out to be far more common than expected. The new implementation is based on a hash map of std::type_info::name() strings, which should perform equally well in the success and failure cases: no special-case fallback logic. --- indra/llcommon/lltypeinfolookup.h | 119 +++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h index 7510cc12ed..1e62d33488 100644 --- a/indra/llcommon/lltypeinfolookup.h +++ b/indra/llcommon/lltypeinfolookup.h @@ -12,8 +12,12 @@ #if ! defined(LL_LLTYPEINFOLOOKUP_H) #define LL_LLTYPEINFOLOOKUP_H -#include "llsortedvector.h" +#include +#include +#include +#include #include +#include /** * LLTypeInfoLookup is specifically designed for use cases for which you might @@ -22,40 +26,52 @@ * you can't rely on always getting the same std::type_info* for a given type: * different load modules will produce different std::type_info*. * LLTypeInfoLookup contains a workaround to address this issue. - * - * Specifically, when we don't find the passed std::type_info*, - * LLTypeInfoLookup performs a linear search over registered entries to - * compare name() strings. Presuming that this succeeds, we cache the new - * (previously unrecognized) std::type_info* to speed future lookups. - * - * This worst-case fallback search (linear search with string comparison) - * should only happen the first time we look up a given type from a particular - * load module other than the one from which we initially registered types. - * (However, a lookup which wouldn't succeed anyway will always have - * worst-case performance.) This class is probably best used with less than a - * few dozen different types. */ template class LLTypeInfoLookup { + // We present an interface like this: + typedef std::map intf_map_type; + // Use this for our underlying implementation: lookup by + // std::type_info::name() string. Note that we must store a std::pair -- in other words, an intf_map_type::value_type + // pair -- so we can present iterators that do the right thing. + // (This might result in a lookup with one std::type_info* returning an + // iterator to a different std::type_info*, but frankly, my dear, we don't + // give a damn.) + typedef boost::unordered_map impl_map_type; + // Iterator shorthand + typedef typename intf_map_type::iterator intf_iterator; + typedef typename intf_map_type::const_iterator intf_const_iterator; + typedef typename intf_map_type::value_type intf_value_type; + typedef typename impl_map_type::iterator impl_iterator; + typedef typename impl_map_type::const_iterator impl_const_iterator; + typedef typename impl_map_type::value_type impl_value_type; + // Type of function that transforms impl_value_type to intf_value_type + typedef boost::function iterfunc; + typedef boost::function const_iterfunc; + public: typedef LLTypeInfoLookup self; - typedef LLSortedVector vector_type; - typedef typename vector_type::key_type key_type; - typedef typename vector_type::mapped_type mapped_type; - typedef typename vector_type::value_type value_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + typedef typename intf_map_type::key_type key_type; + typedef typename intf_map_type::mapped_type mapped_type; + typedef intf_value_type value_type; + + // Iterators are different because we have to transform + // impl_map_type::iterator to intf_map_type::iterator. + typedef boost::transform_iterator iterator; + typedef boost::transform_iterator const_iterator; LLTypeInfoLookup() {} - iterator begin() { return mVector.begin(); } - iterator end() { return mVector.end(); } - const_iterator begin() const { return mVector.begin(); } - const_iterator end() const { return mVector.end(); } - bool empty() const { return mVector.empty(); } - std::size_t size() const { return mVector.size(); } + iterator begin() { return transform(mMap.begin()); } + iterator end() { return transform(mMap.end()); } + const_iterator begin() const { return transform(mMap.begin()); } + const_iterator end() const { return transform(mMap.end()); } + bool empty() const { return mMap.empty(); } + std::size_t size() const { return mMap.size(); } + // Shorthand -- I've always wished std::map supported this signature. std::pair insert(const std::type_info* key, const VALUE& value) { return insert(value_type(key, value)); @@ -63,48 +79,35 @@ public: std::pair insert(const value_type& pair) { - return mVector.insert(pair); + // Obtain and store the std::type_info::name() string as the key. + // Save the whole incoming pair as the value! + std::pair + inserted(mMap.insert(impl_value_type(pair.first->name(), pair))); + // Have to transform the iterator before returning. + return std::pair(transform(inserted.first), inserted.second); } - // const find() forwards to non-const find(): this can alter mVector! - const_iterator find(const std::type_info* key) const + iterator find(const std::type_info* key) { - return const_cast(this)->find(key); + return transform(mMap.find(key->name())); } - // non-const find() caches previously-unknown type_info* to speed future - // lookups. - iterator find(const std::type_info* key) + const_iterator find(const std::type_info* key) const { - iterator found = mVector.find(key); - if (found != mVector.end()) - { - // If LLSortedVector::find() found, great, we're done. - return found; - } - // Here we didn't find the passed type_info*. On Linux, though, even - // for the same type, typeid(sametype) produces a different type_info* - // when used in different load modules. So the fact that we didn't - // find the type_info* we seek doesn't mean this type isn't - // registered. Scan for matching name() string. - for (typename vector_type::iterator ti(mVector.begin()), tend(mVector.end()); - ti != tend; ++ti) - { - if (std::string(ti->first->name()) == key->name()) - { - // This unrecognized 'key' is for the same type as ti->first. - // To speed future lookups, insert a new entry that lets us - // look up ti->second using this same 'key'. - return insert(key, ti->second).first; - } - } - // We simply have never seen a type with this type_info* from any load - // module. - return mVector.end(); + return transform(mMap.find(key->name())); } private: - vector_type mVector; + iterator transform(impl_iterator iter) + { + return boost::make_transform_iterator(iter, boost::mem_fn(&impl_value_type::second)); + } + const_iterator transform(impl_const_iterator iter) + { + return boost::make_transform_iterator(iter, boost::mem_fn(&impl_value_type::second)); + } + + impl_map_type mMap; }; #endif /* ! defined(LL_LLTYPEINFOLOOKUP_H) */ -- cgit v1.2.3 From 00ae56334c057c1ea5ad08c604b551fcbdf37a30 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Jul 2012 16:41:26 -0400 Subject: MAINT-1175: Fix Windows build. It seems MSVC doesn't like boost::make_transform_iterator() in the context I was using it. Try directly invoking the iterator's constructor. --- indra/llcommon/lltypeinfolookup.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h index 1e62d33488..583ca8863b 100644 --- a/indra/llcommon/lltypeinfolookup.h +++ b/indra/llcommon/lltypeinfolookup.h @@ -100,11 +100,11 @@ public: private: iterator transform(impl_iterator iter) { - return boost::make_transform_iterator(iter, boost::mem_fn(&impl_value_type::second)); + return iterator(iter, boost::mem_fn(&impl_value_type::second)); } const_iterator transform(impl_const_iterator iter) { - return boost::make_transform_iterator(iter, boost::mem_fn(&impl_value_type::second)); + return const_iterator(iter, boost::mem_fn(&impl_value_type::second)); } impl_map_type mMap; -- cgit v1.2.3 From a5ba9c0eb327d2fb38a39560a34712e844a71a79 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 16:56:38 -0400 Subject: SH-3276 Handle 416 status back from texture fetches as okay. A 416 will just mean there's no more data and whatever we have is complete. --- indra/newview/lltexturefetch.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a1f99eeb25..5c39504243 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1364,6 +1364,10 @@ bool LLTextureFetchWorker::doWork(S32 param) } mState = SEND_HTTP_REQ; acquireHttpSemaphore(); + + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. } if (mState == WAIT_HTTP_RESOURCE2) @@ -1486,13 +1490,16 @@ bool LLTextureFetchWorker::doWork(S32 param) { LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; + } else { llinfos << "HTTP GET failed for: " << mUrl << " Status: " << mGetStatus.toHex() << " Reason: '" << mGetReason << "'" - // *FIXME: Add retry info for reporting purposes... - // << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } @@ -1839,7 +1846,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe success = false; std::string reason(status.toString()); setGetStatus(status, reason); - llwarns << "CURL GET FAILED, status: " << status.toHex() + llwarns << "CURL GET FAILED, status: " << status.toHex() << " reason: " << reason << llendl; } else -- cgit v1.2.3 From 70035274093e8803f8c7f28162feef311ef725b4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Jul 2012 17:29:58 -0400 Subject: MAINT-1175: Still grappling with MSVC idiosyncracies. Maybe it's failing to correctly handle overloaded transform() methods? --- indra/llcommon/lltypeinfolookup.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h index 583ca8863b..679cc51f1d 100644 --- a/indra/llcommon/lltypeinfolookup.h +++ b/indra/llcommon/lltypeinfolookup.h @@ -66,8 +66,8 @@ public: iterator begin() { return transform(mMap.begin()); } iterator end() { return transform(mMap.end()); } - const_iterator begin() const { return transform(mMap.begin()); } - const_iterator end() const { return transform(mMap.end()); } + const_iterator begin() const { return const_transform(mMap.begin()); } + const_iterator end() const { return const_transform(mMap.end()); } bool empty() const { return mMap.empty(); } std::size_t size() const { return mMap.size(); } @@ -94,7 +94,7 @@ public: const_iterator find(const std::type_info* key) const { - return transform(mMap.find(key->name())); + return const_transform(mMap.find(key->name())); } private: @@ -102,7 +102,7 @@ private: { return iterator(iter, boost::mem_fn(&impl_value_type::second)); } - const_iterator transform(impl_const_iterator iter) + const_iterator const_transform(impl_const_iterator iter) { return const_iterator(iter, boost::mem_fn(&impl_value_type::second)); } -- cgit v1.2.3 From bc72acbfd2410e01946375bcfa29cf37a7c01c17 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 10 Jul 2012 18:50:21 -0400 Subject: SH-3244 Syscall avoidance in HttpRequest::update() method Well, achieved that by doing work in bulk when needed. But turned into some additional things. Change timebase from mS to uS as, well, things are headed that way. Implement an HttpReplyQueue::fetchAll method (advertised one, hadn't implemented it). --- indra/llcorehttp/_httpreplyqueue.cpp | 20 +++++++++++ indra/llcorehttp/_httprequestqueue.cpp | 7 ++-- indra/llcorehttp/examples/http_texture_load.cpp | 2 +- indra/llcorehttp/httprequest.cpp | 40 +++++++++++++++++---- indra/llcorehttp/httprequest.h | 7 ++-- indra/llcorehttp/tests/test_httprequest.hpp | 48 ++++++++++++------------- indra/newview/llappviewer.cpp | 2 +- indra/newview/lltexturefetch.cpp | 19 ++++------ 8 files changed, 92 insertions(+), 53 deletions(-) diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp index a354ed7e10..558b7bdee9 100644 --- a/indra/llcorehttp/_httpreplyqueue.cpp +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -84,4 +84,24 @@ HttpOperation * HttpReplyQueue::fetchOp() return result; } + +void HttpReplyQueue::fetchAll(OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + if (! mQueue.empty()) + { + mQueue.swap(ops); + } + } + + // Caller also acquires the reference counts on each op. + return; +} + + } // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp index 9acac665a9..c16966d078 100644 --- a/indra/llcorehttp/_httprequestqueue.cpp +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -120,10 +120,9 @@ HttpOperation * HttpRequestQueue::fetchOp(bool wait) void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) { - // Note: Should probably test whether we're empty or not here. - // A target passed in with entries is likely also carrying - // reference counts and we're going to leak something. - ops.clear(); + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + { HttpScopedLock lock(mQueueMutex); diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index e5951e8415..bcb322bd5c 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -244,7 +244,7 @@ int main(int argc, char** argv) int passes(0); while (! ws.reload(hr)) { - hr->update(5000); + hr->update(5000000); ms_sleep(2); if (0 == (++passes % 200)) { diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp index 3a55a849b9..9b739a8825 100644 --- a/indra/llcorehttp/httprequest.cpp +++ b/indra/llcorehttp/httprequest.cpp @@ -296,17 +296,43 @@ HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) } -HttpStatus HttpRequest::update(long millis) +HttpStatus HttpRequest::update(long usecs) { - const HttpTime limit(totalTime() + (1000 * HttpTime(millis))); HttpOperation * op(NULL); - while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + + if (usecs) { - // Process operation - op->visitNotifier(this); + const HttpTime limit(totalTime() + HttpTime(usecs)); + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); - // We're done with the operation - op->release(); + // We're done with the operation + op->release(); + } + } + else + { + // Same as above, just no time limit + HttpReplyQueue::OpContainer replies; + mReplyQueue->fetchAll(replies); + if (! replies.empty()) + { + for (HttpReplyQueue::OpContainer::iterator iter(replies.begin()); + replies.end() != iter; + ++iter) + { + // Swap op pointer for NULL; + op = *iter; *iter = NULL; + + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } } return HttpStatus(); diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h index 134a61b618..9dd53f4483 100644 --- a/indra/llcorehttp/httprequest.h +++ b/indra/llcorehttp/httprequest.h @@ -341,13 +341,14 @@ public: /// are expected to return 'quickly' and do any significant processing /// outside of the notification callback to onCompleted(). /// - /// @param millis Maximum number of wallclock milliseconds to + /// @param usecs Maximum number of wallclock microseconds to /// spend in the call. As hinted at above, this /// is partly a function of application code so it's - /// a soft limit. + /// a soft limit. A '0' value will run without + /// time limit. /// /// @return Standard status code. - HttpStatus update(long millis); + HttpStatus update(long usecs); /// @} diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index 914f35ec3d..ba7c757af4 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -260,7 +260,7 @@ void HttpRequestTestObjectType::test<3>() int limit(20); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -275,7 +275,7 @@ void HttpRequestTestObjectType::test<3>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -358,8 +358,8 @@ void HttpRequestTestObjectType::test<4>() int limit(20); while (count++ < limit && mHandlerCalls < 2) { - req1->update(1000); - req2->update(1000); + req1->update(1000000); + req2->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -375,8 +375,8 @@ void HttpRequestTestObjectType::test<4>() limit = 100; while (count++ < limit && mHandlerCalls < 3) { - req1->update(1000); - req2->update(1000); + req1->update(1000000); + req2->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -459,7 +459,7 @@ void HttpRequestTestObjectType::test<5>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("NoOp notification received", mHandlerCalls == 1); @@ -535,7 +535,7 @@ void HttpRequestTestObjectType::test<6>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("No notifications received", mHandlerCalls == 0); @@ -616,7 +616,7 @@ void HttpRequestTestObjectType::test<7>() int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -632,7 +632,7 @@ void HttpRequestTestObjectType::test<7>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -733,7 +733,7 @@ void HttpRequestTestObjectType::test<8>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -749,7 +749,7 @@ void HttpRequestTestObjectType::test<8>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -843,7 +843,7 @@ void HttpRequestTestObjectType::test<9>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -859,7 +859,7 @@ void HttpRequestTestObjectType::test<9>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -955,7 +955,7 @@ void HttpRequestTestObjectType::test<10>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -971,7 +971,7 @@ void HttpRequestTestObjectType::test<10>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1074,7 +1074,7 @@ void HttpRequestTestObjectType::test<11>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1090,7 +1090,7 @@ void HttpRequestTestObjectType::test<11>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1194,7 +1194,7 @@ void HttpRequestTestObjectType::test<12>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1210,7 +1210,7 @@ void HttpRequestTestObjectType::test<12>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1316,7 +1316,7 @@ void HttpRequestTestObjectType::test<13>() int limit(10); while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1333,7 +1333,7 @@ void HttpRequestTestObjectType::test<13>() limit = 10; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); @@ -1435,7 +1435,7 @@ void HttpRequestTestObjectType::test<14>() int limit(50); // With one retry, should fail quickish while (count++ < limit && mHandlerCalls < 1) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Request executed in reasonable time", count < limit); @@ -1451,7 +1451,7 @@ void HttpRequestTestObjectType::test<14>() limit = 100; while (count++ < limit && mHandlerCalls < 2) { - req->update(1000); + req->update(1000000); usleep(100000); } ensure("Second request executed in reasonable time", count < limit); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8243d4f2f3..0549a972e1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5398,7 +5398,7 @@ void CoreHttp::cleanup() { while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) { - mRequest->update(200); + mRequest->update(200000); ms_sleep(50); } if (! mStopped) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 5c39504243..8314031f14 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2740,22 +2740,14 @@ void LLTextureFetch::commonUpdate() // Run a cross-thread command, if any. cmdDoWork(); - // Update Curl on same thread as mCurlGetRequest was constructed - LLCore::HttpStatus status = mHttpRequest->update(200); + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); if (! status) { LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " << status.toString() << LL_ENDL; } - -#if 0 - // *FIXME: maybe implement this another way... - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } -#endif } @@ -2840,7 +2832,8 @@ void LLTextureFetch::endThread() void LLTextureFetch::threadedUpdate() { llassert_always(mHttpRequest); - + +#if 0 // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -2849,9 +2842,10 @@ void LLTextureFetch::threadedUpdate() return; } process_timer.reset(); +#endif commonUpdate(); - + #if 0 const F32 INFO_TIME = 1.0f; static LLFrameTimer info_timer; @@ -2865,7 +2859,6 @@ void LLTextureFetch::threadedUpdate() } } #endif - } ////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 18bd525d00ee3ce16164900293ee6ea8c2204589 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Jul 2012 08:14:34 -0400 Subject: MAINT-1175: Forbid LLRegistry[Singleton]. Back out code that selects LLTypeInfoLookup for the underlying map implementation when KEY = [const] std::type_info*, because LLTypeInfoLookup's API is changing to become incompatible with std::map. Instead, fail with STATIC_ASSERT when LLRegistry's KEY is [const] std::type_info*. Fix all existing uses to use std::type_info::name() string instead. --- indra/llcommon/llregistry.h | 30 ++++++++++-------------------- indra/llui/llfloater.cpp | 2 +- indra/llui/llradiogroup.cpp | 2 +- indra/llui/llrngwriter.cpp | 2 +- indra/llui/llscrolllistcolumn.cpp | 2 +- indra/llui/lltextbase.cpp | 2 +- indra/llui/lltooltip.cpp | 2 +- indra/llui/lluictrlfactory.cpp | 6 +++--- indra/llui/lluictrlfactory.h | 4 ++-- indra/llui/llxuiparser.cpp | 2 +- indra/llui/llxuiparser.h | 3 ++- indra/newview/llavatarlistitem.cpp | 2 +- indra/newview/llinventorylistitem.cpp | 2 +- indra/newview/llwearableitemslist.cpp | 8 ++++---- 14 files changed, 30 insertions(+), 39 deletions(-) diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 36d7f7a44c..843c169f3d 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -30,8 +30,8 @@ #include #include +#include #include "llsingleton.h" -#include "lltypeinfolookup.h" template class LLRegistryDefaultComparator @@ -39,27 +39,17 @@ class LLRegistryDefaultComparator bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; } }; -template -struct LLRegistryMapSelector -{ - typedef std::map type; -}; - -template -struct LLRegistryMapSelector -{ - typedef LLTypeInfoLookup type; -}; - -template -struct LLRegistryMapSelector -{ - typedef LLTypeInfoLookup type; -}; - template > class LLRegistry { + // Do not use LLRegistry with KEY = std::type_info* or KEY = const std::type_info*. + // This is known to fail on Linux. + // If you must use LLRegistry with dynamic type info, use KEY = const char* + // and pass std::type_info::name(); this works across load modules. + // Disallow both std::type_info* and const std::type_info*. First remove + // the pointer, then remove const, then compare is_same. + BOOST_STATIC_ASSERT(! (boost::is_same::type>::type, std::type_info>::value)); + public: typedef LLRegistry registry_t; typedef typename boost::add_reference::type>::type ref_const_key_t; @@ -72,7 +62,7 @@ public: { friend class LLRegistry; public: - typedef typename LLRegistryMapSelector::type registry_map_t; + typedef std::map registry_map_t; bool add(ref_const_key_t key, ref_const_value_t value) { diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 8ca1e685a9..f20cc2452f 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -232,7 +232,7 @@ void LLFloater::initClass() } // defaults for floater param block pulled from widgets/floater.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(typeid(LLFloater::Params).name(), "floater"); LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) : LLPanel(), // intentionally do not pass params here, see initFromParams diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index 95a7d09382..71bbcd8ade 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -66,7 +66,7 @@ protected: LLSD mPayload; // stores data that this item represents in the radio group }; -static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item"); +static LLWidgetNameRegistry::StaticRegistrar register_radio_item(typeid(LLRadioGroup::ItemParams).name(), "radio_item"); LLRadioGroup::Params::Params() : allow_deselect("allow_deselect"), diff --git a/indra/llui/llrngwriter.cpp b/indra/llui/llrngwriter.cpp index 5e6840d7df..90a1e77b1a 100644 --- a/indra/llui/llrngwriter.cpp +++ b/indra/llui/llrngwriter.cpp @@ -92,7 +92,7 @@ void LLRNGWriter::addDefinition(const std::string& type_name, const LLInitParam: // add includes for all possible children const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name); - const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type); + const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type->name()); // add include declarations for all valid children for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp index 07a6dfaa10..d132d0cc7e 100644 --- a/indra/llui/llscrolllistcolumn.cpp +++ b/indra/llui/llscrolllistcolumn.cpp @@ -39,7 +39,7 @@ const S32 MIN_COLUMN_WIDTH = 20; // defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(typeid(LLScrollColumnHeader::Params).name(), "scroll_column_header"); //--------------------------------------------------------------------------- // LLScrollColumnHeader diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 7aeeae298f..8314386632 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -131,7 +131,7 @@ struct LLTextBase::line_end_compare // // register LLTextBase::Params under name "textbase" -static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(typeid(LLTextBase::Params).name(), "textbase"); LLTextBase::LineSpacingParams::LineSpacingParams() : multiple("multiple", 1.f), diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index f737d48abf..0723dbec70 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -129,7 +129,7 @@ void LLToolTipView::drawStickyRect() } // defaults for floater param block pulled from widgets/floater.xml -static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(typeid(LLInspector::Params).name(), "inspector"); // // LLToolTip diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 25e7a31e90..fe3b470722 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -281,7 +281,7 @@ const LLInitParam::BaseBlock& get_empty_param_block() void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag) { // associate parameter block type with template .xml file - std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type); + std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type->name()); if (existing_tag != NULL) { if(*existing_tag != tag) @@ -297,7 +297,7 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st return; } } - LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag); + LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type->name(), tag); //FIXME: comment this in when working on schema generation //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type); //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block); @@ -306,6 +306,6 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st //static const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type) { - return LLWidgetNameRegistry::instance().getValue(widget_type); + return LLWidgetNameRegistry::instance().getValue(widget_type->name()); } diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index d612ad5005..1f7a8e08ce 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -69,9 +69,9 @@ protected: friend class LLSingleton; }; -// lookup widget name by type +// lookup widget name by type (actually by std::type_info::name()) class LLWidgetNameRegistry -: public LLRegistrySingleton +: public LLRegistrySingleton {}; // lookup function for generating empty param block by widget type diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index afc76024d1..c3a246b9a5 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -606,7 +606,7 @@ void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& p // add includes for all possible children const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name); - const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type); + const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type->name()); // add choices for valid children if (widget_registryp) diff --git a/indra/llui/llxuiparser.h b/indra/llui/llxuiparser.h index d7cd256967..3c092afdd6 100644 --- a/indra/llui/llxuiparser.h +++ b/indra/llui/llxuiparser.h @@ -55,8 +55,9 @@ typedef boost::function widget_registry_t; +// const char* key used for std::type_info::name() string class LLChildRegistryRegistry -: public LLRegistrySingleton +: public LLRegistrySingleton {}; diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 30eecfe323..c07fec8e5e 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -44,7 +44,7 @@ S32 LLAvatarListItem::sLeftPadding = 0; S32 LLAvatarListItem::sNameRightPadding = 0; S32 LLAvatarListItem::sChildrenWidths[LLAvatarListItem::ALIC_COUNT]; -static LLWidgetNameRegistry::StaticRegistrar sRegisterAvatarListItemParams(&typeid(LLAvatarListItem::Params), "avatar_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterAvatarListItemParams(typeid(LLAvatarListItem::Params).name(), "avatar_list_item"); LLAvatarListItem::Params::Params() : default_style("default_style"), diff --git a/indra/newview/llinventorylistitem.cpp b/indra/newview/llinventorylistitem.cpp index 3e0849a795..5489671430 100644 --- a/indra/newview/llinventorylistitem.cpp +++ b/indra/newview/llinventorylistitem.cpp @@ -40,7 +40,7 @@ #include "llinventorymodel.h" #include "llviewerinventory.h" -static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelInventoryListItemBaseParams(&typeid(LLPanelInventoryListItemBase::Params), "inventory_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelInventoryListItemBaseParams(typeid(LLPanelInventoryListItemBase::Params).name(), "inventory_list_item"); static const S32 WIDGET_SPACING = 3; diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 92697fb2eb..abb53301a6 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -135,7 +135,7 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name, ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelClothingListItem(&typeid(LLPanelClothingListItem::Params), "clothing_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelClothingListItem(typeid(LLPanelClothingListItem::Params).name(), "clothing_list_item"); LLPanelClothingListItem::Params::Params() @@ -222,7 +222,7 @@ BOOL LLPanelClothingListItem::postBuild() ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelBodyPartsListItem(&typeid(LLPanelBodyPartsListItem::Params), "bodyparts_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelBodyPartsListItem(typeid(LLPanelBodyPartsListItem::Params).name(), "bodyparts_list_item"); LLPanelBodyPartsListItem::Params::Params() @@ -293,7 +293,7 @@ BOOL LLPanelBodyPartsListItem::postBuild() return TRUE; } -static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDeletableWearableListItem(&typeid(LLPanelDeletableWearableListItem::Params), "deletable_wearable_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDeletableWearableListItem(typeid(LLPanelDeletableWearableListItem::Params).name(), "deletable_wearable_list_item"); LLPanelDeletableWearableListItem::Params::Params() : delete_btn("delete_btn") @@ -373,7 +373,7 @@ void LLPanelAttachmentListItem::updateItem(const std::string& name, ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDummyClothingListItem(&typeid(LLPanelDummyClothingListItem::Params), "dummy_clothing_list_item"); +static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelDummyClothingListItem(typeid(LLPanelDummyClothingListItem::Params).name(), "dummy_clothing_list_item"); LLPanelDummyClothingListItem::Params::Params() : add_panel("add_panel"), -- cgit v1.2.3 From 578d70dec0a01b5ed7b461c38503c082ac1a3608 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Jul 2012 08:20:14 -0400 Subject: MAINT-1175: Change LLTypeInfoLookup API for future optimizations. Per discussion with Richard, accept the type key for insert() and find() as a template parameter rather than as std::type_info*. This permits (e.g.) some sort of compile-time prehashing for common types, without changing the API. Eliminate iterators from the API altogether, thus avoiding costs associated with transform_iterator. Fix existing references in llinitparam.h. --- indra/llcommon/llinitparam.h | 24 +++++------ indra/llcommon/lltypeinfolookup.h | 90 +++++++++++---------------------------- 2 files changed, 38 insertions(+), 76 deletions(-) diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index 99983a19cb..c0170e533b 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -242,20 +242,20 @@ namespace LLInitParam template bool readValue(T& param) { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); - if (found_it != mParserReadFuncs->end()) + boost::optional found_it = mParserReadFuncs->find(); + if (found_it) { - return found_it->second(*this, (void*)¶m); + return (*found_it)(*this, (void*)¶m); } return false; } template bool writeValue(const T& param, name_stack_t& name_stack) { - parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); - if (found_it != mParserWriteFuncs->end()) + boost::optional found_it = mParserWriteFuncs->find(); + if (found_it) { - return found_it->second(*this, (const void*)¶m, name_stack); + return (*found_it)(*this, (const void*)¶m, name_stack); } return false; } @@ -263,10 +263,10 @@ namespace LLInitParam // dispatch inspection to registered inspection functions, for each parameter in a param block template bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) { - parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T)); - if (found_it != mParserInspectFuncs->end()) + boost::optional found_it = mParserInspectFuncs->find(); + if (found_it) { - found_it->second(name_stack, min_count, max_count, possible_values); + (*found_it)(name_stack, min_count, max_count, possible_values); return true; } return false; @@ -281,14 +281,14 @@ namespace LLInitParam template void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL) { - mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func)); - mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func)); + mParserReadFuncs->insert(read_func); + mParserWriteFuncs->insert(write_func); } template void registerInspectFunc(parser_inspect_func_t inspect_func) { - mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func)); + mParserInspectFuncs->insert(inspect_func); } bool mParseSilently; diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h index 679cc51f1d..5267e3d2fb 100644 --- a/indra/llcommon/lltypeinfolookup.h +++ b/indra/llcommon/lltypeinfolookup.h @@ -13,11 +13,8 @@ #define LL_LLTYPEINFOLOOKUP_H #include -#include -#include -#include +#include #include -#include /** * LLTypeInfoLookup is specifically designed for use cases for which you might @@ -26,87 +23,52 @@ * you can't rely on always getting the same std::type_info* for a given type: * different load modules will produce different std::type_info*. * LLTypeInfoLookup contains a workaround to address this issue. + * + * The API deliberately diverges from std::map in several respects: + * * It avoids iterators, not only begin()/end() but also as return values + * from insert() and find(). This bypasses transform_iterator overhead. + * * Since we literally use compile-time types as keys, the essential insert() + * and find() methods accept the key type as a @em template parameter, + * accepting and returning value_type as a normal runtime value. This is to + * permit future optimization (e.g. compile-time type hashing) without + * changing the API. */ template class LLTypeInfoLookup { - // We present an interface like this: - typedef std::map intf_map_type; // Use this for our underlying implementation: lookup by - // std::type_info::name() string. Note that we must store a std::pair -- in other words, an intf_map_type::value_type - // pair -- so we can present iterators that do the right thing. - // (This might result in a lookup with one std::type_info* returning an - // iterator to a different std::type_info*, but frankly, my dear, we don't - // give a damn.) - typedef boost::unordered_map impl_map_type; - // Iterator shorthand - typedef typename intf_map_type::iterator intf_iterator; - typedef typename intf_map_type::const_iterator intf_const_iterator; - typedef typename intf_map_type::value_type intf_value_type; - typedef typename impl_map_type::iterator impl_iterator; - typedef typename impl_map_type::const_iterator impl_const_iterator; - typedef typename impl_map_type::value_type impl_value_type; - // Type of function that transforms impl_value_type to intf_value_type - typedef boost::function iterfunc; - typedef boost::function const_iterfunc; + // std::type_info::name() string. This is one of the rare cases in which I + // dare use const char* directly, rather than std::string, because I'm + // sure that every value returned by std::type_info::name() is static. + typedef boost::unordered_map impl_map_type; public: - typedef LLTypeInfoLookup self; - typedef typename intf_map_type::key_type key_type; - typedef typename intf_map_type::mapped_type mapped_type; - typedef intf_value_type value_type; - - // Iterators are different because we have to transform - // impl_map_type::iterator to intf_map_type::iterator. - typedef boost::transform_iterator iterator; - typedef boost::transform_iterator const_iterator; + typedef VALUE value_type; LLTypeInfoLookup() {} - iterator begin() { return transform(mMap.begin()); } - iterator end() { return transform(mMap.end()); } - const_iterator begin() const { return const_transform(mMap.begin()); } - const_iterator end() const { return const_transform(mMap.end()); } bool empty() const { return mMap.empty(); } std::size_t size() const { return mMap.size(); } - // Shorthand -- I've always wished std::map supported this signature. - std::pair insert(const std::type_info* key, const VALUE& value) - { - return insert(value_type(key, value)); - } - - std::pair insert(const value_type& pair) + template + bool insert(const value_type& value) { // Obtain and store the std::type_info::name() string as the key. - // Save the whole incoming pair as the value! - std::pair - inserted(mMap.insert(impl_value_type(pair.first->name(), pair))); - // Have to transform the iterator before returning. - return std::pair(transform(inserted.first), inserted.second); - } - - iterator find(const std::type_info* key) - { - return transform(mMap.find(key->name())); + // Return just the bool from std::map::insert()'s return pair. + return mMap.insert(typename impl_map_type::value_type(typeid(KEY).name(), value)).second; } - const_iterator find(const std::type_info* key) const + template + boost::optional find() const { - return const_transform(mMap.find(key->name())); + // Use the std::type_info::name() string as the key. + typename impl_map_type::const_iterator found = mMap.find(typeid(KEY).name()); + if (found == mMap.end()) + return boost::optional(); + return found->second; } private: - iterator transform(impl_iterator iter) - { - return iterator(iter, boost::mem_fn(&impl_value_type::second)); - } - const_iterator const_transform(impl_const_iterator iter) - { - return const_iterator(iter, boost::mem_fn(&impl_value_type::second)); - } - impl_map_type mMap; }; -- cgit v1.2.3 From 709c1eeae90dae106800e3742f3655bd7b590b7b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Jul 2012 14:13:45 -0400 Subject: MAINT-1175: Properly pass LLRegistry's COMPARATOR to underlying map. Although LLRegistry and LLRegistrySingleton have always defined a COMPARATOR template parameter, it wasn't used for the underlying map. Therefore every type, including any pointer type, was being compared using std::less. This happens to work most of the time -- but is tripping us up now. Pass COMPARATOR to underlying std::map. Fix a couple minor bugs in LLRegistryDefaultComparator (never before used!). Specialize for const char*. Remove CompareTypeID and LLCompareTypeID because we now actively forbid using LLRegistry; remove only known reference (LLWidgetNameRegistry definition). --- indra/llcommon/llinitparam.h | 8 -------- indra/llcommon/llregistry.h | 16 +++++++++++++--- indra/llui/lluictrlfactory.h | 11 +---------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index c0170e533b..66c72c2d9f 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -212,14 +212,6 @@ namespace LLInitParam public: - struct CompareTypeID - { - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return lhs->before(*rhs); - } - }; - typedef std::vector > name_stack_t; typedef std::pair name_stack_range_t; typedef std::vector possible_values_t; diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 843c169f3d..8eeab59024 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -34,9 +34,19 @@ #include "llsingleton.h" template -class LLRegistryDefaultComparator +struct LLRegistryDefaultComparator { - bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; } + bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } +}; + +// comparator for const char* registry keys +template <> +struct LLRegistryDefaultComparator +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) < 0; + } }; template > @@ -62,7 +72,7 @@ public: { friend class LLRegistry; public: - typedef std::map registry_map_t; + typedef std::map registry_map_t; bool add(ref_const_key_t key, ref_const_value_t value) { diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 1f7a8e08ce..a5fd83e555 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -34,15 +34,6 @@ class LLView; -// sort functor for typeid maps -struct LLCompareTypeID -{ - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return lhs->before(*rhs); - } -}; - // lookup widget constructor funcs by widget name template class LLChildRegistry : public LLRegistrySingleton @@ -71,7 +62,7 @@ protected: // lookup widget name by type (actually by std::type_info::name()) class LLWidgetNameRegistry -: public LLRegistrySingleton +: public LLRegistrySingleton {}; // lookup function for generating empty param block by widget type -- cgit v1.2.3 From 7010459f04177aef1875a110b3d33e10c8ec5cad Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 11 Jul 2012 15:53:57 -0400 Subject: SH-3240 Capture Content-Type and Content-Encoding headers. HttpResponse object now has two strings for these content headers. Either or both may be empty. Tidied up the cross-platform string code and got more defensive about the length of a header line. Integration test for the new response object. --- indra/llcorehttp/_httpoprequest.cpp | 158 ++++++++++++++++++------- indra/llcorehttp/_httpoprequest.h | 4 + indra/llcorehttp/httpresponse.h | 19 ++- indra/llcorehttp/tests/test_httprequest.hpp | 126 ++++++++++++++++++++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 2 +- 5 files changed, 263 insertions(+), 46 deletions(-) diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp index bec82d8449..1854d7ada4 100644 --- a/indra/llcorehttp/_httpoprequest.cpp +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -74,16 +74,16 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line); -#if LL_WINDOWS +// OS-neutral string comparisons of various types +int os_strncasecmp(const char *s1, const char *s2, size_t n); +int os_strcasecmp(const char *s1, const char *s2); +char * os_strtok_r(char *str, const char *delim, char **saveptr); -// Not available on windows where the legacy strtok interface -// is thread-safe. -char *strtok_r(char *str, const char *delim, char **saveptr); -#endif // LL_WINDOWS +static const char * const hdr_whitespace(" \t"); +static const char * const hdr_separator(": \t"); - -} +} // end anonymous namespace namespace LLCore @@ -228,7 +228,8 @@ void HttpOpRequest::visitNotifier(HttpRequest * request) // Got an explicit offset/length in response response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); } - + response->setContent(mReplyConType, mReplyConEncode); + mUserHandler->onCompleted(static_cast(this), response); response->release(); @@ -315,7 +316,7 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, HttpOptions * options, HttpHeaders * headers) { - mProcFlags = 0; + mProcFlags = PF_SCAN_CONTENT_HEADERS; // Always scan for content headers mReqPolicy = policy_id; mReqPriority = priority; mReqURL = url; @@ -377,6 +378,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) mReplyHeaders->release(); mReplyHeaders = NULL; } + mReplyConType.clear(); + mReplyConEncode.clear(); // *FIXME: better error handling later HttpStatus status; @@ -539,7 +542,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service) } curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); - if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_SCAN_CONTENT_HEADERS)) { curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); @@ -598,12 +601,18 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi static const char con_ran_line[] = "content-range:"; static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + static const char con_type_line[] = "content-type:"; + static const size_t con_type_line_len = sizeof(con_type_line) - 1; + + static const char con_enc_line[] = "content-encoding:"; + static const size_t con_enc_line_len = sizeof(con_enc_line) - 1; HttpOpRequest * op(static_cast(userdata)); const size_t hdr_size(size * nmemb); const char * hdr_data(static_cast(data)); // Not null terminated - + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) { // One of possibly several status lines. Reset what we know and start over @@ -611,24 +620,47 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi op->mReplyOffset = 0; op->mReplyLength = 0; op->mReplyFullLength = 0; + op->mReplyConType.clear(); + op->mReplyConEncode.clear(); op->mStatus = HttpStatus(); if (op->mReplyHeaders) { op->mReplyHeaders->mHeaders.clear(); } } - else if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + + // Nothing in here wants a final CR/LF combination. Remove + // it as much as possible. + size_t wanted_hdr_size(hdr_size); + if (wanted_hdr_size && '\n' == hdr_data[wanted_hdr_size - 1]) + { + if (--wanted_hdr_size && '\r' == hdr_data[wanted_hdr_size - 1]) + { + --wanted_hdr_size; + } + } + + // Save header if caller wants them in the response + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size)); + } + + // Detect and parse 'Content-Range' headers + if (op->mProcFlags & PF_SCAN_RANGE_HEADER) { char hdr_buffer[128]; // Enough for a reasonable header - size_t frag_size((std::min)(hdr_size, sizeof(hdr_buffer) - 1)); + size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1)); memcpy(hdr_buffer, hdr_data, frag_size); hdr_buffer[frag_size] = '\0'; -#if LL_WINDOWS - if (! _strnicmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#else - if (! strncasecmp(hdr_buffer, con_ran_line, (std::min)(frag_size, con_ran_line_len))) -#endif // LL_WINDOWS + if (frag_size > con_ran_line_len && + ! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len)) { unsigned int first(0), last(0), length(0); int status; @@ -656,22 +688,43 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi } } - if (op->mProcFlags & PF_SAVE_HEADERS) + // Detect and parse 'Content-Type' and 'Content-Encoding' headers + if (op->mProcFlags & PF_SCAN_CONTENT_HEADERS) { - // Save headers in response - if (! op->mReplyHeaders) + if (wanted_hdr_size > con_type_line_len && + ! os_strncasecmp(hdr_data, con_type_line, con_type_line_len)) { - op->mReplyHeaders = new HttpHeaders; + // Found 'Content-Type:', extract single-token value + std::string rhs(hdr_data + con_type_line_len, wanted_hdr_size - con_type_line_len); + std::string::size_type begin(0), end(rhs.size()), pos; + + if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) + { + begin = pos; + } + if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) + { + end = pos; + } + op->mReplyConType.assign(rhs, begin, end - begin); } - size_t wanted_size(hdr_size); - if (wanted_size && '\n' == hdr_data[wanted_size - 1]) + else if (wanted_hdr_size > con_enc_line_len && + ! os_strncasecmp(hdr_data, con_enc_line, con_enc_line_len)) { - if (--wanted_size && '\r' == hdr_data[wanted_size - 1]) + // Found 'Content-Encoding:', extract single-token value + std::string rhs(hdr_data + con_enc_line_len, wanted_hdr_size - con_enc_line_len); + std::string::size_type begin(0), end(rhs.size()), pos; + + if ((pos = rhs.find_first_not_of(hdr_whitespace)) != std::string::npos) { - --wanted_size; + begin = pos; } + if ((pos = rhs.find_first_of(hdr_whitespace, begin)) != std::string::npos) + { + end = pos; + } + op->mReplyConEncode.assign(rhs, begin, end - begin); } - op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_size)); } return hdr_size; @@ -788,15 +841,11 @@ int parse_content_range_header(char * buffer, char * tok_state(NULL), * tok(NULL); bool match(true); - if (! strtok_r(buffer, ": \t", &tok_state)) + if (! os_strtok_r(buffer, hdr_separator, &tok_state)) match = false; - if (match && (tok = strtok_r(NULL, " \t", &tok_state))) -#if LL_WINDOWS - match = 0 == _stricmp("bytes", tok); -#else - match = 0 == strcasecmp("bytes", tok); -#endif // LL_WINDOWS - if (match && ! (tok = strtok_r(NULL, " \t", &tok_state))) + if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state))) + match = 0 == os_strcasecmp("bytes", tok); + if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state))) match = false; if (match) { @@ -834,15 +883,6 @@ int parse_content_range_header(char * buffer, return 1; } -#if LL_WINDOWS - -char *strtok_r(char *str, const char *delim, char ** savestate) -{ - return strtok_s(str, delim, savestate); -} - -#endif // LL_WINDOWS - void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) { @@ -880,6 +920,36 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::strin } +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ +#if LL_WINDOWS + return _strnicmp(s1, s2, n); +#else + return strncasecmp(s1, s2, n); +#endif // LL_WINDOWS +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ +#if LL_WINDOWS + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif // LL_WINDOWS +} + + +char * os_strtok_r(char *str, const char *delim, char ** savestate) +{ +#if LL_WINDOWS + return strtok_s(str, delim, savestate); +#else + return strtok_r(str, delim, savestate); +#endif +} + + } // end anonymous namespace diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h index a4c5fbb3c2..200b925c4e 100644 --- a/indra/llcorehttp/_httpoprequest.h +++ b/indra/llcorehttp/_httpoprequest.h @@ -30,6 +30,7 @@ #include "linden_common.h" // Modifies curl/curl.h interfaces +#include #include #include "httpcommon.h" @@ -137,6 +138,7 @@ protected: unsigned int mProcFlags; static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + static const unsigned int PF_SCAN_CONTENT_HEADERS = 0x00000004U; public: // Request data @@ -162,6 +164,8 @@ public: size_t mReplyLength; size_t mReplyFullLength; HttpHeaders * mReplyHeaders; + std::string mReplyConType; + std::string mReplyConEncode; // Policy data int mPolicyRetries; diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h index 65e403cec8..3d35050c17 100644 --- a/indra/llcorehttp/httpresponse.h +++ b/indra/llcorehttp/httpresponse.h @@ -28,6 +28,8 @@ #define _LLCORE_HTTP_RESPONSE_H_ +#include + #include "httpcommon.h" #include "_refcounted.h" @@ -130,7 +132,20 @@ public: mReplyLength = length; mReplyFullLength = full_length; } - + + /// + void getContent(std::string & con_type, std::string & con_encode) const + { + con_type = mContentType; + con_encode = mContentEncoding; + } + + void setContent(const std::string & con_type, const std::string & con_encode) + { + mContentType = con_type; + mContentEncoding = con_encode; + } + protected: // Response data here HttpStatus mStatus; @@ -139,6 +154,8 @@ protected: unsigned int mReplyFullLength; BufferArray * mBufferArray; HttpHeaders * mHeaders; + std::string mContentType; + std::string mContentEncoding; }; diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp index ba7c757af4..9edf3d19ec 100644 --- a/indra/llcorehttp/tests/test_httprequest.hpp +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -117,6 +117,15 @@ public: ensure("Special header X-LL-Special in response", found_special); } + if (! mCheckContentType.empty()) + { + ensure("Response required with content type check", response != NULL); + std::string con_type, con_enc; + response->getContent(con_type, con_enc); + ensure("Content-Type as expected (" + mCheckContentType + ")", + mCheckContentType == con_type); + } + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; } @@ -124,6 +133,7 @@ public: std::string mName; HttpHandle mExpectHandle; bool mCheckHeader; + std::string mCheckContentType; }; typedef test_group HttpRequestTestGroupType; @@ -1502,6 +1512,122 @@ void HttpRequestTestObjectType::test<14>() } } +// Test retrieval of Content-Type/Content-Encoding headers +template <> template <> +void HttpRequestTestObjectType::test<15>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Content-Type"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // Load and clear the string setting to preload std::string object + // for memory return tests. + handler.mCheckContentType = "application/llsd+xml"; + handler.mCheckContentType.clear(); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mCheckContentType = "application/llsd+xml"; + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckContentType.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + } // end namespace tut namespace diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 9e1847c66e..58b5bedd09 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -9,7 +9,7 @@ $LicenseInfo:firstyear=2008&license=viewerlgpl$ Second Life Viewer Source Code -Copyright (C) 2010, Linden Research, Inc. +Copyright (C) 2012, Linden Research, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public -- cgit v1.2.3 From c0e2d4d988ba6d7d28bfa0487006349266e80709 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 11 Jul 2012 15:14:13 -0500 Subject: MAINT-794 Fix for assert on loading some types of objects before their parents. --- indra/newview/lldrawable.cpp | 3 +-- indra/newview/llviewerobject.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 82ea5cd5a6..4eda2b92b3 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -625,8 +625,6 @@ BOOL LLDrawable::updateMove() return FALSE; } - makeActive(); - BOOL done; if (isState(MOVE_UNDAMPED)) @@ -635,6 +633,7 @@ BOOL LLDrawable::updateMove() } else { + makeActive(); done = updateMoveDamped(); } return done; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index f5a3681437..b52c9d0d4b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -782,7 +782,13 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) } LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; - + + if (parentp && mDrawable->isActive()) + { + parentp->makeActive(); + parentp->setState(LLDrawable::ACTIVE_CHILD); + } + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); if( (old_parent != parentp && old_parent) || (parentp && parentp->isActive())) -- cgit v1.2.3 From e86b1c18db0629d64f8bf26fcf69e64072d084e6 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 11 Jul 2012 15:17:13 -0500 Subject: MAINT-794 Fix for assert on loading some types of objects before their parents. --- indra/llrender/llshadermgr.cpp | 39 ++++++++++++++-------- indra/llrender/llvertexbuffer.cpp | 6 ++-- .../shaders/class1/objects/indexedTextureV.glsl | 4 +-- indra/newview/llface.cpp | 11 +++--- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 3fbf34296f..b6a9a6b653 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -702,7 +702,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade if (texture_index_channels > 1) { - text[count++] = strdup("VARYING_FLAT ivec4 vary_texture_index;\n"); + text[count++] = strdup("VARYING_FLAT int vary_texture_index;\n"); } text[count++] = strdup("vec4 diffuseLookup(vec2 texcoord)\n"); @@ -716,20 +716,33 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade } else if (major_version > 1 || minor_version >= 30) { //switches are supported in GLSL 1.30 and later - text[count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n"); - text[count++] = strdup("\tswitch (vary_texture_index.r)\n"); - text[count++] = strdup("\t{\n"); - - //switch body - for (S32 i = 0; i < texture_index_channels; ++i) - { - std::string case_str = llformat("\t\tcase %d: ret = texture2D(tex%d, texcoord); break;\n", i, i); - text[count++] = strdup(case_str.c_str()); + if (gGLManager.mIsNVIDIA) + { //switches are unreliable on some NVIDIA drivers + for (U32 i = 0; i < texture_index_channels; ++i) + { + std::string if_string = llformat("\t%sif (vary_texture_index == %d) { return texture2D(tex%d, texcoord); }\n", i > 0 ? "else " : "", i, i); + text[count++] = strdup(if_string.c_str()); + } + text[count++] = strdup("\treturn vec4(1,0,1,1);\n"); + text[count++] = strdup("}\n"); } + else + { + text[count++] = strdup("\tvec4 ret = vec4(1,0,1,1);\n"); + text[count++] = strdup("\tswitch (vary_texture_index)\n"); + text[count++] = strdup("\t{\n"); + + //switch body + for (S32 i = 0; i < texture_index_channels; ++i) + { + std::string case_str = llformat("\t\tcase %d: return texture2D(tex%d, texcoord);\n", i, i); + text[count++] = strdup(case_str.c_str()); + } - text[count++] = strdup("\t}\n"); - text[count++] = strdup("\treturn ret;\n"); - text[count++] = strdup("}\n"); + text[count++] = strdup("\t}\n"); + text[count++] = strdup("\treturn ret;\n"); + text[count++] = strdup("}\n"); + } } else { //should never get here. Indexed texture rendering requires GLSL 1.30 or later diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 0092df6587..fd106ab79b 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1326,7 +1326,7 @@ void LLVertexBuffer::setupVertexArray() 1, //TYPE_WEIGHT, 4, //TYPE_WEIGHT4, 4, //TYPE_CLOTHWEIGHT, - 4, //TYPE_TEXTURE_INDEX + 1, //TYPE_TEXTURE_INDEX }; U32 attrib_type[] = @@ -1343,7 +1343,7 @@ void LLVertexBuffer::setupVertexArray() GL_FLOAT, //TYPE_WEIGHT, GL_FLOAT, //TYPE_WEIGHT4, GL_FLOAT, //TYPE_CLOTHWEIGHT, - GL_UNSIGNED_BYTE, //TYPE_TEXTURE_INDEX + GL_UNSIGNED_INT, //TYPE_TEXTURE_INDEX }; bool attrib_integer[] = @@ -2400,7 +2400,7 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) #if !LL_DARWIN S32 loc = TYPE_TEXTURE_INDEX; void *ptr = (void*) (base + mOffsets[TYPE_VERTEX] + 12); - glVertexAttribIPointer(loc, 4, GL_UNSIGNED_BYTE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr); + glVertexAttribIPointer(loc, 1, GL_UNSIGNED_INT, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr); #endif } if (data_mask & MAP_VERTEX) diff --git a/indra/newview/app_settings/shaders/class1/objects/indexedTextureV.glsl b/indra/newview/app_settings/shaders/class1/objects/indexedTextureV.glsl index 7c0699d72f..ca29bf3143 100644 --- a/indra/newview/app_settings/shaders/class1/objects/indexedTextureV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/indexedTextureV.glsl @@ -23,9 +23,9 @@ * $/LicenseInfo$ */ -ATTRIBUTE ivec4 texture_index; +ATTRIBUTE int texture_index; -VARYING_FLAT ivec4 vary_texture_index; +VARYING_FLAT int vary_texture_index; void passTextureIndex() { diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index f5b217d539..4b107ae151 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1943,15 +1943,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLVector4a texIdx; - U8 index = mTextureIndex < 255 ? mTextureIndex : 0; + S32 index = mTextureIndex < 255 ? mTextureIndex : 0; F32 val = 0.f; - U8* vp = (U8*) &val; - vp[0] = index; - vp[1] = 0; - vp[2] = 0; - vp[3] = 0; - + S32* vp = (S32*) &val; + *vp = index; + llassert(index <= LLGLSLShader::sIndexedTextureChannels-1); LLVector4Logical mask; -- cgit v1.2.3 From 0cbeee19d79978ea143804737633dcb5afc8af9c Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Wed, 11 Jul 2012 16:09:07 -0600 Subject: fix for SH-3137: delay to issue new http fetching requests if http connection failure happens --- indra/newview/lltexturefetch.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 09754fc5c0..b148b8b76d 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1276,6 +1276,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { mState = INIT ; mCanUseHTTP = false ; + mUrl.clear(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false ; } @@ -1304,6 +1305,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mHTTPFailCount >= max_attempts) { + mUrl.clear(); if (cur_size > 0) { // Use available data @@ -1327,6 +1329,14 @@ bool LLTextureFetchWorker::doWork(S32 param) } } + // Clear the url since we're done with the fetch + // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch + // next time the texture is requested, even if the data have already been fetched. + if(mWriteToCacheState != NOT_WRITE) + { + mUrl.clear(); + } + llassert_always(mBufferSize == cur_size + mRequestedSize); if(!mBufferSize)//no data received. { @@ -1753,13 +1763,7 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, { mRequestedSize = -1; // error } - // Clear the url since we're done with the fetch - // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch - // next time the texture is requested, even if the data have already been fetched. - if(mWriteToCacheState != NOT_WRITE) - { - mUrl.clear(); - } + mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -- cgit v1.2.3 From e4cea7631293b1192bfe720146410c1aa61654aa Mon Sep 17 00:00:00 2001 From: Jonathan Yap Date: Wed, 11 Jul 2012 18:58:41 -0400 Subject: STORM-1898 Add "Copy SLURL" to context menu when right clicking Landmarks in Inventory --- indra/newview/llinventorybridge.cpp | 28 ++++++++++++++++++++++ indra/newview/llinventorybridge.h | 3 ++- .../skins/default/xui/en/menu_inventory.xml | 8 +++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b86c453d61..657e149319 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -71,6 +71,9 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llwearablelist.h" +#include "lllandmarkactions.h" + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); // Marketplace outbox current disabled #define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 @@ -1396,6 +1399,29 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } + else if ("copy_slurl" == action) + { + LLViewerInventoryItem* item = static_cast(getItem()); + if(item) + { + LLUUID asset_id = item->getAssetUUID(); + LLLandmark* landmark = gLandmarkList.getAsset(asset_id); + if (landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + LLLandmarkActions::getSLURLfromPosGlobal(global_pos, ©_slurl_to_clipboard_callback_inv, true); + } + } + } +} + +void copy_slurl_to_clipboard_callback_inv(const std::string& slurl) +{ + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); } void LLItemBridge::selectItem() @@ -4397,6 +4423,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } items.push_back(std::string("Landmark Separator")); + items.push_back(std::string("url_copy")); items.push_back(std::string("About Landmark")); } @@ -4405,6 +4432,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // info panel can be shown at a time. if ((flags & FIRST_SELECTED_ITEM) == 0) { + disabled_items.push_back(std::string("url_copy")); disabled_items.push_back(std::string("About Landmark")); } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index dc9e88d54d..118430efe1 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -197,6 +197,8 @@ public: const LLUUID& uuid) : LLInvFVBridge(inventory, root, uuid) {} + typedef boost::function slurl_callback_t; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void selectItem(); virtual void restoreItem(); @@ -214,7 +216,6 @@ public: virtual BOOL isItemCopyable() const; virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } LLViewerInventoryItem* getItem() const; diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index b13bf5b508..13dc0b941a 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -537,6 +537,14 @@ layout="topleft" name="Landmark Separator" /> + + + -- cgit v1.2.3 From 79a171209f41189adfeb1ba8e70c8570d380cdc5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Jul 2012 13:19:26 -0400 Subject: MAINT-1175: Linux viewer built on TC is broken, built on dev box works. Try to diagnose the cause of the misbehavior with a BOOST_STATIC_ASSERT. --- indra/llcommon/llregistry.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 8eeab59024..babc1b87aa 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -36,6 +36,8 @@ template struct LLRegistryDefaultComparator { + // It would be Bad if this comparison were used for const char* + BOOST_STATIC_ASSERT(! (boost::is_same::type>::type, char>::value)); bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; -- cgit v1.2.3 From d45b2e7caece787dce4be501b103432c0f06c0f2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 12 Jul 2012 17:46:53 +0000 Subject: SH-3183 Use valgrind on the library. Using http_texture_load as the test subject, library looks clean. Did some better shutdown in the program itself and it looks better. Libcurl itself is making a lot of noise. Adapted testrunner to run valgrind as well but the memory allocation tester in the tools themselves grossly interferes with Valgrind operations. --- indra/llcorehttp/examples/http_texture_load.cpp | 3 +++ indra/llcorehttp/tests/test_llcorehttp_peer.py | 14 +++++++++++++- indra/llcorehttp/tests/testrunner.py | 5 ++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp index bcb322bd5c..998dc9240b 100644 --- a/indra/llcorehttp/examples/http_texture_load.cpp +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -269,7 +269,10 @@ int main(int argc, char** argv) << std::endl; // Clean up + hr->requestStopThread(NULL); + ms_sleep(1000); delete hr; + LLCore::HttpRequest::destroyService(); term_curl(); return 0; diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 58b5bedd09..489e8b2979 100644 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -33,6 +33,7 @@ import os import sys import time import select +import getopt from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn @@ -152,6 +153,13 @@ class Server(ThreadingMixIn, HTTPServer): allow_reuse_address = False if __name__ == "__main__": + do_valgrind = False + path_search = False + options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"]) + for option, value in options: + if option == "-V" or option == "--valgrind": + do_valgrind = True + # Instantiate a Server(TestHTTPRequestHandler) on the first free port # in the specified port range. Doing this inline is better than in a # daemon thread: if it blows up here, we'll get a traceback. If it blew up @@ -159,10 +167,14 @@ if __name__ == "__main__": # subject test program anyway. httpd, port = freeport(xrange(8000, 8020), lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + # Pass the selected port number to the subject test program via the # environment. We don't want to impose requirements on the test program's # command-line parsing -- and anyway, for C++ integration tests, that's # performed in TUT code rather than our own. os.environ["LL_TEST_PORT"] = str(port) debug("$LL_TEST_PORT = %s", port) - sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:])) + if do_valgrind: + args = ["valgrind", "--log-file=./valgrind.log"] + args + path_search = True + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py index 5b9beb359b..9a2de71142 100644 --- a/indra/llcorehttp/tests/testrunner.py +++ b/indra/llcorehttp/tests/testrunner.py @@ -168,7 +168,10 @@ def run(*args, **kwds): # executable passed as our first arg, # - [no e] child should inherit this process's environment. debug("Running %s...", " ".join(args)) - rc = os.spawnv(os.P_WAIT, args[0], args) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) debug("%s returned %s", args[0], rc) return rc -- cgit v1.2.3 From 0660f646e11e9b9afacd228ad43bd31c8c555ad7 Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Thu, 12 Jul 2012 17:54:38 -0600 Subject: fix for SH-3237: cached sculpties do not load at the correct LOD. --- indra/newview/llviewertexture.cpp | 3 ++- indra/newview/llvovolume.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 077056b66a..27415270e6 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1355,9 +1355,10 @@ void LLViewerFetchedTexture::setForSculpt() static const S32 MAX_INTERVAL = 8 ; //frames mForSculpt = TRUE ; - if(isForSculptOnly() && !getBoundRecently()) + if(isForSculptOnly() && hasGLTexture() && !getBoundRecently()) { destroyGLTexture() ; //sculpt image does not need gl texture. + mTextureState = ACTIVE; } checkCachedRawSculptImage() ; setMaxVirtualSizeResetInterval(MAX_INTERVAL) ; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index b5d173346e..2f13f2b1a6 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -833,7 +833,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) } } - S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture + S32 texture_discard = mSculptTexture->getCachedRawImageLevel(); //try to match the texture S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ; if (texture_discard >= 0 && //texture has some data available @@ -1116,7 +1116,7 @@ void LLVOVolume::sculpt() S8 sculpt_components = 0; const U8* sculpt_data = NULL; - S32 discard_level = mSculptTexture->getDiscardLevel() ; + S32 discard_level = mSculptTexture->getCachedRawImageLevel() ; LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ; S32 max_discard = mSculptTexture->getMaxDiscardLevel(); -- cgit v1.2.3 From 8356cc1c4fd921cac46d9d98bdf80c7c8ae05b22 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 13 Jul 2012 12:38:26 -0400 Subject: Try reorganizing panel_login.xml per Leo's redesign. This includes replacing the small "Sign up" link with a "CREATE YOUR ACCOUNT: Start now" button, requiring a small tweak to the C++ code to Do The Right Thing when the button (vs. the link) is clicked. --- indra/newview/llpanellogin.cpp | 5 +- indra/newview/skins/default/xui/en/panel_login.xml | 171 ++++++++++++--------- 2 files changed, 104 insertions(+), 72 deletions(-) diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index a7aa99dfde..e37155aeb6 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -175,8 +175,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLTextBox* forgot_password_text = getChild("forgot_password_text"); forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); - LLTextBox* create_new_account_text = getChild("create_new_account_text"); - create_new_account_text->setClickedCallback(onClickNewAccount, NULL); + childSetAction("create_new_account_btn", onClickNewAccount, NULL); LLTextBox* need_help_text = getChild("login_help"); need_help_text->setClickedCallback(onClickHelp, NULL); @@ -1025,7 +1024,7 @@ void LLPanelLogin::updateLoginPanelLinks() // need to call through sInstance, as it's called from onSelectServer, which // is static. - sInstance->getChildView("create_new_account_text")->setVisible( system_grid); + sInstance->getChildView("create_new_account_btn")->setVisible( system_grid); sInstance->getChildView("forgot_password_text")->setVisible( system_grid); } diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 994eb909e2..64ecdbf752 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -42,16 +42,28 @@ auto_resize="false" follows="left|bottom" name="login" layout="topleft" -width="705" -min_width="705" +width="865" +min_width="865" height="80"> +LOG IN + + Username: @@ -66,19 +78,21 @@ tool_tip="The username you chose when you registered, like bobsmith12 or Steller top_pad="0" name="username_combo" width="178"> + + +top="35" +left="175" +width="150"> Password: - - + + + - - - - - - - - - - + + + + 1, Total number of fetched textures: [NUM] + + + 2, Total number of fetching requests: [NUM] + + + 3, Total number of cache hits: [NUM] + + + 4, Total number of visible textures: [NUM] + + + 5, Total number of visible texture fetching requests: [NUM] + + + 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB + + + 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 9, Total time on cache readings: [TIME] seconds + + + 10, Total time on cache writings: [TIME] seconds + + + 11, Total time on decodings: [TIME] seconds + + + 12, Total time on gl texture creation: [TIME] seconds + + + 13, Total time on HTTP fetching: [TIME] seconds + + + 14, Total time on entire fetching: [TIME] seconds + + + 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_text_editor.xml b/indra/newview/skins/default/xui/en/menu_text_editor.xml index fe8489166b..70b40dd89b 100644 --- a/indra/newview/skins/default/xui/en/menu_text_editor.xml +++ b/indra/newview/skins/default/xui/en/menu_text_editor.xml @@ -1,6 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -291,6 +300,12 @@ function="SideTray.PanelPeopleTab" parameter="nearby_panel" /> + + + - - - - @@ -3125,12 +3131,6 @@ - - - diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a26c5bb344..91b4d38e97 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2358,6 +2358,93 @@ Would you be my friend? + + addlist + Name for the new list: + confirm +
+ +
- \ No newline at end of file + + + + diff --git a/indra/newview/skins/default/xui/en/panel_region_estate.xml b/indra/newview/skins/default/xui/en/panel_region_estate.xml index bfd796a62b..76a82212ae 100644 --- a/indra/newview/skins/default/xui/en/panel_region_estate.xml +++ b/indra/newview/skins/default/xui/en/panel_region_estate.xml @@ -149,11 +149,11 @@ diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index 6ecb57b41d..14bd349480 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -48,7 +48,7 @@ height="300" width="330" /> - Load Files Choose Directory Scripts + Dictionaries + [User] + diff --git a/indra/newview/skins/default/xui/en/teleport_strings.xml b/indra/newview/skins/default/xui/en/teleport_strings.xml index dce6b8dd6d..fdf41991cd 100644 --- a/indra/newview/skins/default/xui/en/teleport_strings.xml +++ b/indra/newview/skins/default/xui/en/teleport_strings.xml @@ -45,6 +45,9 @@ Go to 'Welcome Island Public' to repeat the tutorial. The inventory system is currently unavailable. + + You must be age 18 or over to enter this region. + @@ -80,5 +83,8 @@ Go to 'Welcome Island Public' to repeat the tutorial. Requesting Teleport... - + + Pending Teleport... + + -- cgit v1.2.3 From 0f3c5e6458acdf1676f0b7a6dc42ed8cfa286cb0 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 30 Jul 2012 14:54:44 -0400 Subject: line munging for merge warn-on-failure:unix-eol --- .../xui/en/floater_texture_fetch_debugger.xml | 682 ++++++++++----------- 1 file changed, 341 insertions(+), 341 deletions(-) diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml index f3f8d4ddca..44b6a63bca 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml @@ -1,341 +1,341 @@ - - - - 1, Total number of fetched textures: [NUM] - - - 2, Total number of fetching requests: [NUM] - - - 3, Total number of cache hits: [NUM] - - - 4, Total number of visible textures: [NUM] - - - 5, Total number of visible texture fetching requests: [NUM] - - - 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels - - - 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB - - - 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels - - - 9, Total time on cache readings: [TIME] seconds - - - 10, Total time on cache writings: [TIME] seconds - - - 11, Total time on decodings: [TIME] seconds - - - 12, Total time on gl texture creation: [TIME] seconds - - - 13, Total time on HTTP fetching: [TIME] seconds - - - 14, Total time on entire fetching: [TIME] seconds - - - 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels - - - 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels - - - - - - - - - - - - - - - + + + + 1, Total number of fetched textures: [NUM] + + + 2, Total number of fetching requests: [NUM] + + + 3, Total number of cache hits: [NUM] + + + 4, Total number of visible textures: [NUM] + + + 5, Total number of visible texture fetching requests: [NUM] + + + 6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB + + + 8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels + + + 9, Total time on cache readings: [TIME] seconds + + + 10, Total time on cache writings: [TIME] seconds + + + 11, Total time on decodings: [TIME] seconds + + + 12, Total time on gl texture creation: [TIME] seconds + + + 13, Total time on HTTP fetching: [TIME] seconds + + + 14, Total time on entire fetching: [TIME] seconds + + + 15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + 16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels + + + + + + + + + + + + + + + -- cgit v1.2.3 From 1d950fad52bef2fbb0edd6846a6d7d1d3037b5d0 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Tue, 31 Jul 2012 17:14:37 -0700 Subject: For mac, viewers on 10.5 will pull mac_legacy rather than mac --- .../viewer_components/updater/llupdatechecker.cpp | 25 ++++++++++++++++------ indra/viewer_components/updater/llupdatechecker.h | 2 +- .../viewer_components/updater/llupdaterservice.cpp | 2 +- .../updater/tests/llupdaterservice_test.cpp | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp index c6aa9b0f11..4da774a5f6 100644 --- a/indra/viewer_components/updater/llupdatechecker.cpp +++ b/indra/viewer_components/updater/llupdatechecker.cpp @@ -30,7 +30,9 @@ #include "llsd.h" #include "llupdatechecker.h" #include "lluri.h" - +#if LL_DARWIN +#include +#endif #if LL_WINDOWS #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -55,7 +57,7 @@ class LLUpdateChecker::Implementation: public: Implementation(Client & client); ~Implementation(); - void check(std::string const & protocolVersion, std::string const & hostUrl, + void checkVersion(std::string const & protocolVersion, std::string const & hostUrl, std::string const & servicePath, std::string channel, std::string version); // Responder: @@ -91,10 +93,10 @@ LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): } -void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, +void LLUpdateChecker::checkVersion(std::string const & protocolVersion, std::string const & hostUrl, std::string const & servicePath, std::string channel, std::string version) { - mImplementation->check(protocolVersion, hostUrl, servicePath, channel, version); + mImplementation->checkVersion(protocolVersion, hostUrl, servicePath, channel, version); } @@ -120,7 +122,7 @@ LLUpdateChecker::Implementation::~Implementation() } -void LLUpdateChecker::Implementation::check(std::string const & protocolVersion, std::string const & hostUrl, +void LLUpdateChecker::Implementation::checkVersion(std::string const & protocolVersion, std::string const & hostUrl, std::string const & servicePath, std::string channel, std::string version) { llassert(!mInProgress); @@ -179,7 +181,18 @@ std::string LLUpdateChecker::Implementation::buildUrl(std::string const & protoc #ifdef LL_WINDOWS static const char * platform = "win"; #elif LL_DARWIN - static const char * platform = "mac"; + long versMin; + Gestalt(gestaltSystemVersionMinor, &versMin); + + static const char *platform; + if (versMin == 5) //OS 10.5 + { + platform = "mac_legacy"; + } + else + { + platform = "mac"; + } #else static const char * platform = "lnx"; #endif diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h index cea1f13647..d882169068 100644 --- a/indra/viewer_components/updater/llupdatechecker.h +++ b/indra/viewer_components/updater/llupdatechecker.h @@ -44,7 +44,7 @@ public: LLUpdateChecker(Client & client); // Check status of current app on the given host for the channel and version provided. - void check(std::string const & protocolVersion, std::string const & hostUrl, + void checkVersion(std::string const & protocolVersion, std::string const & hostUrl, std::string const & servicePath, std::string channel, std::string version); private: diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp index 2e18218667..bc73c72ddc 100644 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -509,7 +509,7 @@ bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event) } else { - mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion); + mUpdateChecker.checkVersion(mProtocolVersion, mUrl, mPath, mChannel, mVersion); setState(LLUpdaterService::CHECKING_FOR_UPDATE); } } diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp index 8ffb5cacc2..7c016fecf9 100644 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -44,7 +44,7 @@ *****************************************************************************/ LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client) {} -void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, +void LLUpdateChecker::checkVersion(std::string const & protocolVersion, std::string const & hostUrl, std::string const & servicePath, std::string channel, std::string version) {} LLUpdateDownloader::LLUpdateDownloader(Client & ) {} -- cgit v1.2.3 From bf004be1023347bcabaae6baa1624b2ed78d69fd Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 1 Aug 2012 12:38:28 -0400 Subject: SH-3308 Beef up retry messaging. Reformatted messages around request retry. Successfully retried requests also message so you can see the cycle closed. Added additional retryable error codes (timeout, other libcurl failures). Commenting and removed some unnecessary std::min logic. --- indra/llcorehttp/_httplibcurl.cpp | 10 ++++++++-- indra/llcorehttp/_httppolicy.cpp | 34 ++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp index 4e2e3f0e0e..6fe0bfc7d1 100644 --- a/indra/llcorehttp/_httplibcurl.cpp +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -97,6 +97,12 @@ void HttpLibcurl::start(int policy_count) } +// Give libcurl some cycles, invoke it's callbacks, process +// completed requests finalizing or issuing retries as needed. +// +// If active list goes empty *and* we didn't queue any +// requests for retry, we return a request for a hard +// sleep otherwise ask for a normal polling interval. HttpService::ELoopSpeed HttpLibcurl::processTransport() { HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); @@ -129,7 +135,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() if (completeRequest(mMultiHandles[policy_class], handle, result)) { // Request is still active, don't get too sleepy - ret = (std::min)(ret, HttpService::NORMAL); + ret = HttpService::NORMAL; } handle = NULL; // No longer valid on return } @@ -150,7 +156,7 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport() if (! mActiveOps.empty()) { - ret = (std::min)(ret, HttpService::NORMAL); + ret = HttpService::NORMAL; } return ret; } diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp index c7a69ad133..76c1e22431 100644 --- a/indra/llcorehttp/_httppolicy.cpp +++ b/indra/llcorehttp/_httppolicy.cpp @@ -162,8 +162,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op) const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); op->mPolicyRetryAt = now + delta; ++op->mPolicyRetries; - LL_WARNS("CoreHttp") << "URL op retry #" << op->mPolicyRetries - << " being scheduled for " << delta << " uSecs from now." + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " retry " << op->mPolicyRetries + << " scheduled for +" << (delta / HttpTime(1000)) + << " mS. Status: " << op->mStatus.toHex() << LL_ENDL; if (op->mTracing > 0) { @@ -175,6 +177,17 @@ void HttpPolicy::retryOp(HttpOpRequest * op) } +// Attempt to deliver requests to the transport layer. +// +// Tries to find HTTP requests for each policy class with +// available capacity. Starts with the retry queue first +// looking for requests that have waited long enough then +// moves on to the ready queue. +// +// If all queues are empty, will return an indication that +// the worker thread may sleep hard otherwise will ask for +// normal polling frequency. +// HttpService::ELoopSpeed HttpPolicy::processReadyQueue() { const HttpTime now(totalTime()); @@ -311,6 +324,9 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); + static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); + static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); // Retry or finalize if (! op->mStatus) @@ -323,7 +339,10 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) cant_res_proxy == op->mStatus || cant_res_host == op->mStatus || send_error == op->mStatus || - recv_error == op->mStatus)) + recv_error == op->mStatus || + upload_failed == op->mStatus || + op_timedout == op->mStatus || + post_error == op->mStatus)) { // Okay, worth a retry. We include 499 in this test as // it's the old 'who knows?' error from many grid services... @@ -335,14 +354,17 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) // This op is done, finalize it delivering it to the reply queue... if (! op->mStatus) { - LL_WARNS("CoreHttp") << "URL op failed after " << op->mPolicyRetries + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " failed after " << op->mPolicyRetries << " retries. Reason: " << op->mStatus.toString() + << " (" << op->mStatus.toHex() << ")" << LL_ENDL; } else if (op->mPolicyRetries) { - LL_DEBUGS("CoreHttp") << "URL op succeeded after " << op->mPolicyRetries << " retries." - << LL_ENDL; + LL_WARNS("CoreHttp") << "HTTP request " << static_cast(op) + << " succeeded on retry " << op->mPolicyRetries << "." + << LL_ENDL; } op->stageFromActive(mService); -- cgit v1.2.3 From 27d5547478a816bed87a0f3b0e275c26a2ef581d Mon Sep 17 00:00:00 2001 From: David Kaprielian Date: Wed, 1 Aug 2012 12:56:51 -0700 Subject: Added necessary signing information to autobuild.xml --- autobuild.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autobuild.xml b/autobuild.xml index 9b564ddefc..bd441f5e5f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2125,6 +2125,8 @@ -configuration Release -project SecondLife.xcodeproj + -DENABLE_SIGNING:BOOL=YES + -DSIGNING_IDENTITY:STRING="Developer\ ID\ Application:\ Linden\ Research,\ Inc." configure @@ -2152,6 +2154,8 @@ -configuration Release -project SecondLife.xcodeproj + -DENABLE_SIGNING:BOOL=YES + -DSIGNING_IDENTITY:STRING="Developer\ ID\ Application:\ Linden\ Research,\ Inc." configure -- cgit v1.2.3 From 429726a1ddd5a097f9fe57adb59a44a8155073ea Mon Sep 17 00:00:00 2001 From: eli Date: Wed, 1 Aug 2012 13:20:43 -0700 Subject: sync with viewer-dev, removing obsolete files --- .../skins/default/xui/da/floater_model_wizard.xml | 241 ------ .../skins/default/xui/de/floater_model_wizard.xml | 208 ----- .../skins/default/xui/en/floater_model_wizard.xml | 841 --------------------- .../skins/default/xui/es/floater_model_wizard.xml | 208 ----- .../skins/default/xui/fr/floater_model_wizard.xml | 208 ----- .../skins/default/xui/it/floater_model_wizard.xml | 208 ----- .../skins/default/xui/ja/floater_model_wizard.xml | 208 ----- .../skins/default/xui/pt/floater_model_wizard.xml | 208 ----- .../skins/default/xui/ru/floater_model_wizard.xml | 208 ----- .../skins/default/xui/tr/floater_model_wizard.xml | 208 ----- 10 files changed, 2746 deletions(-) delete mode 100644 indra/newview/skins/default/xui/da/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/de/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/en/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/es/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/fr/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/it/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/ja/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/pt/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/ru/floater_model_wizard.xml delete mode 100644 indra/newview/skins/default/xui/tr/floater_model_wizard.xml diff --git a/indra/newview/skins/default/xui/da/floater_model_wizard.xml b/indra/newview/skins/default/xui/da/floater_model_wizard.xml deleted file mode 100644 index 8ad443581a..0000000000 --- a/indra/newview/skins/default/xui/da/floater_model_wizard.xml +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - - Choose model file - - - - - Advanced users: If you are familiar with 3D content creation tools you may wish to use the Advanced Uploader. - - - - Choose model file to upload - - - - - Second Life supports COLLADA (.dae) files - - - - - - Dimensions (meters): - - - X Y Z - - - - - - WARNING: - - - You will not be able to complete the final step of uploading this model to the Second Life servers. [secondlife:///app/floater/learn_more Find out how] to set up your account for mesh model uploads. - - - - - - - - - Optimize model - - - - We have optimized the model for performance. Adjust it further if you wish. - - - Generating Level of Detail - - - Generate Level of Detail: High - - Generate Level of Detail: Medium - - Generate Level of Detail: Low - - Generate Level of Detail: Lowest - - - Performance - Faster rendering -Less detail -Lower prim weight - Accuracy - Slower rendering -More detail -Higher prim weight - - - ' - - ' - - ' - - - - Geometry preview - - - - - - High detail - - - Medium detail - - - Low detail - - - Lowest detail - - - - - - - - - Adjust physics - - - - We will create a shape for the outer hull of the model. Adjust the shape's detail level as needed for the intended purpose of your model. - - - Performance - Faster rendering -Less detail -Lower prim weight - Accuracy - Slower rendering -More detail -Higher prim weight - - - Examples: -Moving objects -Flying objects -Vehicles - Examples: -Small static objects -Less detailed objects -Simple furniture - Examples: -Static objects -Detailed objects -Buildings - - - - Physics preview - - - - - - High detail - - - Medium detail - - - Low detail - - - Lowest detail - - - - - - - - - Review - - - - Impact to parcel/region: [EQUIV] prim equivalents - - Your account will be charged an upload fee of L$ [FEE]. - - By clicking the upload button, you confirm that you have the appropriate rights to the material contained in the model. - - - - - - - - - - - Upload complete - - - - Your model has been uploaded. - - - You will find it in the Objects folder in your inventory. - - Your account has been charged L$ [FEE]. - - - - - -
- + + + + + + + + + + + + + + + + + + + - Your version of [APP_NAME] does not know how to display the notification it just received. Please verify that you have the latest Viewer installed. + 你的 [APP_NAME] 版本無法顯示它剛剛收到的通知。 請確定你安裝了最新版本的 Viewer。 -Error details: The notification called '[_NAME]' was not found in notifications.xml. +錯誤詳情:在 notifications.xml 裡找不到名為「[_NAME]」的通知。 - Floater error: Could not find the following controls: + 浮動視窗錯誤:找不到以下的控制: [CONTROLS] - No tutorial is currently available. + 目前尚無教學內容。 @@ -68,17 +68,17 @@ Error details: The notification called '[_NAME]' was not found in noti - An error occurred while updating [APP_NAME]. Please [http://get.secondlife.com download the latest version] of the Viewer. + [APP_NAME] 更新時出錯。 請 [http://get.secondlife.com 下載] 最新版本的 Viewer。 - 無法連接到[SECOND_LIFE_GRID]. - '[DIAGNOSTIC]' -請確定你的網際網路是正常運作的。 + 無法連接到 [SECOND_LIFE_GRID]。 + ([DIAGNOSTIC]) +請確定你的網路連線沒有問題。 - Message Template [PATH] not found. + 找不到訊息模板 [PATH]。 @@ -86,49 +86,49 @@ Error details: The notification called '[_NAME]' was not found in noti - There was a problem uploading the text for a script due to the following reason: [REASON]. Please try again later. + 上傳腳本文字時出問題,原因:[REASON]。 請稍候再試一次。 - There was a problem uploading the compiled script due to the following reason: [REASON]. Please try again later. + 上傳已編譯腳本時出問題,原因:[REASON]。 請稍候再試一次。 - There was a problem writing animation data. Please try again later. + 寫入動作資料時出錯。 請稍候再試一次。 - There was a problem uploading the auction snapshot due to the following reason: [REASON] + 上傳拍賣快照時出問題,原因:[REASON] - Unable to view the contents of more than one item at a time. -Please select only one object and try again. + 無法一次瀏覽多個項目的內容。 +請只選擇一個物件,再試一次。 儲存全部服裝或身體部位的變更? - Non-friends won't know that you've choosen to ignore their calls and instant messages. + 不是朋友的人不會知道你選擇忽略他們的通話要求和即時訊息。 - Note: When you turn on this option, anyone who uses this computer can see your list of favorite locations. + 注意:你一旦同意這選項,任何使用這部電腦的人都可看到你有哪些「最愛」地點。 - Granting modify rights to another Resident allows them to change, delete or take ANY objects you may have in-world. Be VERY careful when handing out this permission. -Do you want to grant modify rights for [NAME]? - + 賦予另一居民「修改」權,將允許他更改、刪除或拿取你在虛擬世界裡擁有的任何物件。 賦予這項權限時,敬請慎重考慮。 +你仍要賦予 [NAME] 修改權嗎? + - Granting modify rights to another Resident allows them to change ANY objects you may have in-world. Be VERY careful when handing out this permission. -Do you want to grant modify rights for the selected Residents? + 賦予另一居民「修改」權,將允許他更改你在虛擬世界裡擁有的任何物件。 賦予這項權限時,敬請慎重考慮。 +你仍要將修改權賦予所選居民嗎? - Do you want to revoke modify rights for [NAME]? + 你要撤銷 [NAME] 的修改權嗎? - Do you want to revoke modify rights for the selected Residents? + 你要撤銷所選居民的修改權嗎? @@ -146,33 +146,33 @@ Do you want to grant modify rights for the selected Residents? - You are about to add group members to the role of [ROLE_NAME]. -Members cannot be removed from that role. -The members must resign from the role themselves. -Are you sure you want to continue? - + 你即將賦予群組成員 [ROLE_NAME] 的角色。 +成員一旦獲得此角色,你就不能自行撤銷。 +成員必須自行離職才能撤銷這角色。 +你確定你要繼續? + - You are about to add the Ability '[ACTION_NAME]' to the Role '[ROLE_NAME]'. + 你即將把「[ACTION_NAME]」能力增加給「[ROLE_NAME]」角色。 - *WARNING* - Any Member in a Role with this Ability can assign themselves -- and any other member -- to Roles that have more powers than they currently have, potentially elevating themselves to near-Owner power. Be sure you know what you're doing before assigning this Ability. + *警告* + 任何屬於帶有這能力的角色的成員,都可以賦予自己或任何人新的角色,因此得到比現在更多的權力,最終可能擁有近似「所有人」的權力。 賦予這項能力之前,敬請慎重考慮。 -Add this Ability to '[ROLE_NAME]'? +你仍要新增這項能力給「[ROLE_NAME]」? - You are about to add the Ability '[ACTION_NAME]' to the Role '[ROLE_NAME]'. + 你即將把「[ACTION_NAME]」能力增加給「[ROLE_NAME]」角色。 - *WARNING* - Any Member in a Role with this Ability can assign themselves -- and any other member -- all Abilities, elevating themselves to near-Owner power. + *警告* + 任何屬於帶有這能力的角色的成員,都可以將所有能力賦予自己或任何人,提升為近似「所有人」權力的層級。 -Add this Ability to '[ROLE_NAME]'? +你仍要新增這項能力給「[ROLE_NAME]」? - You are about to drop your attachment. - Are you sure you want to continue? + 你即將卸除你的附件。 + 你確定你要繼續? @@ -181,178 +181,178 @@ Add this Ability to '[ROLE_NAME]'? - You are joining group [NAME]. -Do you wish to proceed? + 你即將加入 [NAME] 群組。 +你確定要繼續嗎? - Joining this group costs L$[COST]. -You do not have enough L$ to join this group. + 加入此群組須花費 L$[COST]。 +你的 L$ 不夠加入這群組。 - Creating this group will cost L$100. -Groups need more than one member, or they are deleted forever. -Please invite members within 48 hours. + 創立這個群組費用為 L$100。 +群組需有至少兩位成員,否則將永久被刪除。 +請在 48 小時內邀請成員加入。 - For L$[COST] you can enter this land ('[PARCEL_NAME]') for [TIME] hours. Buy a pass? + 花費 L$[COST],可進入「[PARCEL_NAME]」土地,停留 [TIME] 小時。 購買通行權? - Sale price must be set to more than L$0 if selling to anyone. -Please select an individual to sell to if selling for L$0. + 若是出售給任何人,售價必須超過 L$0。 +如果售價定為 L$0,請選定一位出售對象。 - The selected [LAND_SIZE] m² land is being set for sale. -Your selling price will be L$[SALE_PRICE] and will be authorized for sale to [NAME]. + 所選的 [LAND_SIZE] 平方公尺土地即將準備出售。 +售價將為 L$[SALE_PRICE],並將經你授權出售給 [NAME]。 - ATTENTION: Clicking 'sell to anyone' makes your land available to the entire [SECOND_LIFE] community, even those not in this region. + 請注意:若點按「出售給任何人」,你的土地將允許整個 [SECOND_LIFE] 社群前來購買,包括此地區外的人。 -The selected [LAND_SIZE] m² land is being set for sale. -Your selling price will be L$[SALE_PRICE] and will be authorized for sale to [NAME]. +所選的 [LAND_SIZE] 平方公尺土地即將準備出售。 +售價將為 L$[SALE_PRICE],並將經你授權出售給 [NAME]。 - Are you sure you want to return all objects shared with the group '[NAME]' on this parcel of land back to their previous owner's inventory? + 你確定要將和本土地地段的「[NAME]」群組分享的所有物件送返到原所有人的收納區? -*WARNING* This will delete the non-transferable objects deeded to the group! +警告:這動作將會刪除原先讓渡給這群組的所有不可轉讓物件! -Objects: [N] +物件:[N] - Are you sure you want to return all objects owned by the Resident '[NAME]' on this parcel of land back to their inventory? + 你確定要將本地段裡「[NAME]」居民所擁有的所有物件送返其收納區? -Objects: [N] +物件:[N] - Are you sure you want to return all objects owned by you on this parcel of land back to your inventory? + 你確定要將本地段裡你所擁有的所有物件送返你的收納區? -Objects: [N] +物件:[N] - Are you sure you want to return all objects NOT owned by you on this parcel of land back to their owner's inventory? -Transferable objects deeded to a group will be returned to their previous owners. + 你確定要將本地段裡不屬於你的所有物件送返其所有人的收納區? +先前讓渡給群組的可轉讓物件將送返給原物主。 -*WARNING* This will delete the non-transferable objects deeded to the group! +警告:這動作將會刪除原先讓渡給這群組的所有不可轉讓物件! -Objects: [N] +物件:[N] - Are you sure you want to return all objects NOT owned by [NAME] on this parcel of land back to their owner's inventory? -Transferable objects deeded to a group will be returned to their previous owners. + 你確定要將本地段裡不屬於 [NAME] 的所有物件送返其所有人的收納區? +先前讓渡給群組的可轉讓物件將送返給原物主。 -*WARNING* This will delete the non-transferable objects deeded to the group! +警告:這動作將會刪除原先讓渡給這群組的所有不可轉讓物件! -Objects: [N] +物件:[N] - Are you sure you want to return all listed objects back to their owner's inventory? + 你確定要將所列的所有物件送返其所有人的收納區? - Are you sure you want to disable all objects in this region? + 你確定要停用本地區裡所有物件? - Return the objects on this parcel of land that are NOT shared with the group [NAME] back to their owners? + 將本地段裡未和 [NAME] 群組分享的物件送返給物主? -Objects: [N] +物件:[N] - Can not disable scripts. -This entire region is damage enabled. -Scripts must be allowed to run for weapons to work. + 無法停用腳本。 +這整個地區允許傷害。 +腳本必須獲准執行,武器才有效。 - Multiple faces are currently selected. -If you continue this action, separate instances of media will be set on multiple faces of the object. -To place the media on only one face, choose Select Face and click on the desired face of that object then click Add. - + 目前選擇了多個臉部。 +如果你繼續,物件的多個臉部將有各自的媒體啟動。 +若想只在一個臉部放置媒體,請「選擇臉部」,點按物件上你所要的臉部,再按「新增」。 + - You must be standing inside the land parcel to set its Landing Point. + 你必須站在地段上才能設定其登陸點。 - Please enter a valid email address for the recipient(s). + 請輸入居民的有效的電郵地址。 - Please enter your email address. + 請輸入你的電郵地址。 - Email snapshot with the default subject or message? + 用預設的電郵主旨或內文送出快照? - Error processing snapshot data + 處理快照資料時出錯 - Error encoding snapshot. + 為快照編碼時出錯。 - There was a problem sending a snapshot due to the following reason: [REASON] + 發送快照時出問題,原因:[REASON] - There was a problem uploading a report screenshot due to the following reason: [REASON] + 上傳舉報用快照時出問題,原因:[REASON] - You must agree to the Terms of Service to continue logging into [SECOND_LIFE]. + 你必須同意服務條款才可繼續登入 [SECOND_LIFE]。 - Could not put on outfit. -The outfit folder contains no clothing, body parts, or attachments. + 無法穿上裝扮。 +裝扮資料夾裡沒有任何衣物、身體部位或附件。 - You can not wear clothes or body parts that are in the trash + 你不能穿戴垃圾桶裡的衣物或身體部位。 - Could not attach object. -Exceeds the attachments limit of [MAX_ATTACHMENTS] objects. Please detach another object first. + 無法附加物件。 +超過 [MAX_ATTACHMENTS] 項物件的附加上限。 請先卸除一個物件。 - You can not wear that item because it has not yet loaded. Please try again in a minute. + 無法穿戴該物件,它尚未完成載入。 請稍候再試。 - Oops! Something was left blank. -You need to enter the Username name of your avatar. + 糟糕! 發現有內容留白。 +你必須為化身輸入一個使用者名稱。 -You need an account to enter [SECOND_LIFE]. Would you like to create one now? +進入 [SECOND_LIFE] 需要一個帳號。 你現在要不要新建一個? - [create_account_url] + http://join.secondlife.com/ - You need to enter either the Username or both the First and Last name of your avatar into the Username field, then login again. + 你必須在「使用者名稱」欄位裡輸入使用者名稱,或輸入化身的名和姓,然後再登入。 - Delete classified '[NAME]'? -There is no reimbursement for fees paid. + 刪除個人廣告「[NAME]」? +已付費用恕不退回。 - You have selected to delete the media associated with this face. -Are you sure you want to continue? + 你選擇刪除置於這臉部的媒體。 +你確定你要繼續? - Save changes to classified [NAME]? + 儲存個人廣告「[NAME]」的變更? - Insufficient funds to create classified. + 金額不足,無法刊登個人廣告。 - Delete pick <nolink>[PICK]</nolink>? + 刪除精選位置<nolink>[PICK]</nolink>? @@ -367,7 +367,7 @@ Are you sure you want to continue? - Please select a proposal to view. + 請選擇一個要察看的提議。 請選擇一個歷史紀錄物品去察看。 @@ -380,13 +380,13 @@ Are you sure you want to continue? 注意:這將會清除快取資料。 - Port settings take effect after you restart [APP_NAME]. + 重新啟動 [APP_NAME] 後將啟用新的埠設定。 - The new skin will appear after you restart [APP_NAME]. + 重新啟動 [APP_NAME] 後將顯現新的皮膚。 - Changing language will take effect after you restart [APP_NAME]. + 重新啟動 [APP_NAME] 後將換成新的語言。 前往 [SECOND_LIFE] 網頁去察看拍賣細節或下標? @@ -396,92 +396,92 @@ Are you sure you want to continue? - Save Changes? + 儲存變更? - Gesture save failed. -This gesture has too many steps. -Try removing some steps, then save again. + 姿勢儲存失敗。 +這個姿勢步驟太多。 +請去除一些步驟再儲存。 - Gesture save failed. Please try again in a minute. + 姿勢儲存失敗。 請稍候再試。 - Could not save gesture because the object or the associated object inventory could not be found. -The object may be out of range or may have been deleted. + 無法儲存姿勢,找不到該物件或所屬物件收納區。 +該物件可能超出範圍或已被刪除。 - There was a problem saving a gesture due to the following reason: [REASON]. Please try resaving the gesture later. + 儲存姿勢時出問題,原因:[REASON]。 請稍後再嘗試儲存姿勢。 - Could not save notecard because the object or the associated object inventory could not be found. -The object may be out of range or may have been deleted. + 無法儲存記事卡,找不到該物件或所屬物件收納區。 +該物件可能超出範圍或已被刪除。 - There was a problem saving a notecard due to the following reason: [REASON]. Please try re-saving the notecard later. + 儲存記事卡時出問題,原因:[REASON]。 請稍後再嘗試儲存記事卡。 - Could not undo all changes in your version of the script. -Would you like to load the server's last saved version? -(**Warning** This operation cannot be undone.) + 無法取消你這個版本腳本的變更。 +是否從伺服器載入上次成功儲存的版本? +(*警告* 這動作無法還原。) - There was a problem saving a script due to the following reason: [REASON]. Please try re-saving the script later. + 儲存腳本時出問題,原因:[REASON]。 請稍後再嘗試儲存腳本。 - Could not save the script because the object it is in could not be found. -The object may be out of range or may have been deleted. + 無法儲存腳本,找不到它所屬的物件。 +該物件可能超出範圍或已被刪除。 - There was a problem saving a compiled script due to the following reason: [REASON]. Please try re-saving the script later. + 儲存編譯腳本時出問題,原因:[REASON]。 請稍後再嘗試儲存腳本。 - Oops, Your Start Region is not defined. -Please type the Region name in Start Location box or choose My Last Location or My Home as your Start Location. + 糟糕,你的起始區域尚未定義。 +請在「開始位置」框裡輸入區域名,或選擇「我上一次位置」或「我的家」作為開始位置。 - Could not start or stop the script because the object it is on could not be found. -The object may be out of range or may have been deleted. + 無法啟動或停止腳本,找不到它所屬的物件。 +該物件可能超出範圍或已被刪除。 - Unable to download file + 無法下載檔案 - Unable to write file [[FILE]] + 無法寫入檔案 [[FILE]] - Just so you know, your computer does not meet [APP_NAME]'s minimum system requirements. You may experience poor performance. Unfortunately, the [SUPPORT_SITE] can't provide technical support for unsupported system configurations. + 要告知你,你的電腦未達 [APP_NAME] 的最低系統需求。 你可能會發現電腦性能不佳。 很遺憾,[SUPPORT_SITE] 無法針對不支援的系統設置提供技術支援。 -Visit [_URL] for more information? +要造訪 [_URL] 獲取進一步資訊? http://www.secondlife.com/corporate/sysreqs.php - Your system contains a graphics card that [APP_NAME] doesn't recognize. -This is often the case with new hardware that hasn't been tested yet with [APP_NAME]. It will probably be ok, but you may need to adjust your graphics settings. -(Me > Preferences > Graphics). + 你的系統含有一個 [APP_NAME] 無法辨認的顯像卡。 +原因很可能是 [APP_NAME] 尚未針對新硬體完成測試。 這大概不會出問題,但你可能需要調整顯像設定。 +(我自己 > 偏好設定 > 顯像)
- [APP_NAME] crashed while initializing graphics drivers. -Graphics Quality will be set to Low to avoid some common driver errors. This will disable some graphics features. -We recommend updating your graphics card drivers. -Graphics Quality can be raised in Preferences > Graphics. + [APP_NAME] 初始化顯像驅動程式時當掉了。 +顯像度將設為低階,以防發生常見的驅動程式或錯誤。 這將停用一些顯像功能。 +我們建議你更新顯像驅動程式。 +請到「偏好設定 > 顯像」提高顯像度。 這個 [REGION] 地區並不允許變更地形。 - You do not have permission to copy the following items: + 你沒有權限複製以下項目: [ITEMS] -and will lose it from your inventory if you give it away. Do you really want to offer these items? +如果你將它送人,它將無法續留在收納區。 你確定要送出這些東西嗎? @@ -491,18 +491,18 @@ and will lose it from your inventory if you give it away. Do you really want to 交易已取消。 - Cannot give more than 42 items in a single inventory transfer. + 每一次的收納區轉移動作不能超過 42 項物件。 - You do not have permission to transfer the selected items. + 你沒有權限轉移所選項目。 - You do not have permission to copy [COUNT] of the selected items. You will lose these items from your inventory. -Do you really want to give these items? + 你沒有權限複製所選物件當中的 [COUNT] 項。 你的收納區將無法續留這些物件。 +你確定要送出這些東西嗎? - You do not have permission to transfer the selected folder. + 你沒有權限轉移所選資料夾。 凍結這位化身? @@ -533,75 +533,75 @@ Do you really want to give these items? 取得錯誤:太多物件被選取。 - ACQUIRE ERROR: Objects span more than one region. -Please move all objects to be acquired onto the same region. + 取得錯誤:物件跨越多個地區。 +請將所欲取得的物件移到同一區域。 [EXTRA] -Go to [_URL] for information on purchasing L$? +前往 [_URL] 得知如何購買 L$? http://secondlife.com/app/currency/ - Unable to link these [COUNT] objects. -You can link a maximum of [MAX] objects. + 無法聯結這 [COUNT] 個物件。 +你最多可以聯結 [MAX] 個物件。 - You can only link complete sets of objects, and must select more than one object. + 可以聯結的必需是一套物件,請選擇至少兩個物件。 - Unable to link because you don't have modify permission on all the objects. + 無法聯結;你對某些物件沒有修改權。 -Please make sure none are locked, and that you own all of them. +請確定沒有物件被鎖住,並確定你擁有所有物件。 - Unable to link because not all of the objects have the same owner. + 無法聯結;有些物件的所有人不同。 -Please make sure you own all of the selected objects. +請確定你是所選所有物件的所有人。 - No file extension for the file: '[FILE]' + 檔案沒有副檔名:'[FILE]' -Please make sure the file has a correct file extension. +請確定檔案具備正確的副檔名。 - Invalid file extension [EXTENSION] -Expected [VALIDS] + 無效的副檔名:[EXTENSION] +應該是 [VALIDS] - Couldn't open uploaded sound file for reading: + 無法開啟並讀取上傳的聲音檔: [FILE] - File does not appear to be a RIFF WAVE file: + 檔案似乎不是 RIFF WAVE 檔: [FILE] - File does not appear to be a PCM WAVE audio file: + 檔案似乎不是 PCM WAVE 音頻檔: [FILE] - File has invalid number of channels (must be mono or stereo): + 檔案的頻道數無效(必需為單聲道或立體聲) [FILE] - File does not appear to be a supported sample rate (must be 44.1k): + 檔案似乎帶有不支援的採樣率(需為 44.1 k): [FILE] - File does not appear to be a supported word size (must be 8 or 16 bit): + 檔案似乎帶有不支援的字組(需為 8 或 16 位元): [FILE] - Could not find 'data' chunk in WAV header: + 找不到 WAV 檔頭的「data」區塊: [FILE] - Wrong chunk size in WAV file: + WAV 檔區塊大小錯誤: [FILE] @@ -609,41 +609,41 @@ Expected [VALIDS] [FILE] - Problem with file [FILE]: + 檔案 [FILE] 有問題: [ERROR] - Couldn't open temporary compressed sound file for writing: [FILE] + 無法開啟並寫入暫時壓縮音頻檔:[FILE] - Unknown Vorbis encode failure on: [FILE] + 發生不明的 Vorbis 編碼錯誤:[FILE] - Unable to encode file: [FILE] + 無法將檔案編碼:[FILE] - We can't fill in your username and password. This may happen when you change network setup + 我們無法填入你的使用者名稱和密碼。 這可能因為你變更了網路設定。 - Corrupt resource file: [FILE] + 毀損的資源檔:[FILE] - Unknown Linden resource file version in file: [FILE] + 檔案中發現不明的 Linden 資源檔版本:[FILE] - Unable to create output file: [FILE] + 無法建立輸出檔:[FILE] - [APP_NAME] does not currently support bulk upload of animation files. + [APP_NAME] 目前尚不支援動作檔批量上傳。 - Unable to upload [FILE] due to the following reason: [REASON] -Please try again later. + 無法上傳 [FILE],原因:[REASON] +請稍候再試一次。 - 你已經添加 "[LANDMARK_NAME]" 到你的 [FOLDER_NAME] 資料夾。 + 你已新增「[LANDMARK_NAME]」到你的 [FOLDER_NAME] 資料夾。 你已經有這個位置的地標。 @@ -653,132 +653,132 @@ Please try again later. 你不能創造地標,因為地主並不允許你這樣做。 - Not able to perform 'recompilation'. -Select an object with a script. + 無法進行重新編譯。 +請選擇帶有腳本的物件。 - Not able to perform 'recompilation'. + 無法進行重新編譯。 -Select objects with scripts that you have permission to modify. +選擇帶有你可修改的腳本的物件。 - Not able to perform 'reset'. + 無法重新啟動。 -Select objects with scripts. +選擇帶有腳本的物件。 - Not able to perform 'reset'. + 無法重新啟動。 -Select objects with scripts that you have permission to modify. +選擇帶有你可修改的腳本的物件。 - Unable to open script in object without modify permissions. + 無法開啟物件裡的腳本,因為你沒有修改權。 - Not able to set any scripts to 'running'. + 無法將任何腳本設定為「執行中」。 -Select objects with scripts. +選擇帶有腳本的物件。 - Unable to set any scripts to 'not running'. + 無法將任何腳本設定為「不執行」。 -Select objects with scripts. +選擇帶有腳本的物件。 - No frontmost floater to save. + 沒有最前置的浮動視窗可儲存。 - Your search query was modified and the words that were too short were removed. + 你的搜尋字串已被修改,太短的字已被移除。 -Searched for: [FINALQUERY] +搜尋:[FINALQUERY] - Your search terms were too short so no search was performed. + 你的搜尋字串太短,所以沒有啟動搜索。 瞬間傳送失敗。 [REASON] - Problem encountered processing your teleport request. You may need to log back in before you can teleport. -If you continue to get this message, please check the [SUPPORT_SITE]. + 處理你瞬間傳送要求時發生問題。 如果想要瞬間傳送,你可能需要重新登入。 +如果你持續得到此訊息,請查閱 [SUPPORT_SITE]。 - Problem encountered processing your region crossing. You may need to log back in before you can cross regions. -If you continue to get this message, please check the [SUPPORT_SITE]. + 處理你跨越地區動作時發生問題。 如果想要跨越地區,你可能需要重新登入。 +如果你持續得到此訊息,請查閱 [SUPPORT_SITE]。 - Sorry, teleport is currently blocked. Try again in a moment. If you still cannot teleport, please log out and log back in to resolve the problem. + 抱歉,目前禁止瞬間傳送。 請稍後再試。 如果你仍無法進行瞬間傳送,請登出後重新入來解決此一問題。 - Sorry, but system was unable to locate landmark destination. + 抱歉,不過系統無法找到地標所在目的地。 - Sorry, but system was unable to complete the teleport connection. Try again in a moment. + 抱歉,不過系統無法完成瞬間傳送的聯接。 請稍後再試。 - Sorry, you do not have access to that teleport destination. + 抱歉,你並沒有權限進入要瞬間傳送的目的地。 - Your attachments have not arrived yet. Try waiting for a few more seconds or log out and back in again before attempting to teleport. + 你的附件尚未抵達。 請稍候一會兒,或請登出後重新登入,再嘗試瞬間傳送。 - The asset queue in this region is currently clogged so your teleport request will not be able to succeed in a timely manner. Please try again in a few minutes or go to a less busy area. + 該地區的資產查詢目前太過擁塞,因此你的瞬間傳送動作可能無法即時發生。 請稍候再試,或請前往較不擁塞的地區。 - Sorry, but the system was unable to complete your teleport request in a timely fashion. Please try again in a few minutes. + 抱歉,系統無法即時完成為你瞬間傳送。 請稍待幾分鐘再試。 - Sorry, but the system was unable to complete your region crossing in a timely fashion. Please try again in a few minutes. + 抱歉,系統無法即時讓你跨越地區。 請稍待幾分鐘再試。 - Unable to find teleport destination. The destination may be temporarily unavailable or no longer exists. Please try again in a few minutes. + 找不到瞬間傳送的目的地。 目的地可能暫時不可用,或已不存在。 請稍待幾分鐘再試。 收納區功能目前無法使用。 - 無法設定土地擁有者: -無地段被選取。 + 無法設定土地所有人: +未選擇地段。 - Unable to force land ownership because selection spans multiple regions. Please select a smaller area and try again. + 無法強設土地所有權;所選部分跨越多個區域。 請縮小選擇範圍,再試一次。 - This parcel is up for auction. Forcing ownership will cancel the auction and potentially make some Residents unhappy if bidding has begun. -Force ownership? + 這地段正在拍賣中。 強設所有權將取消拍賣,這恐將造成某些已出價的居民不高興。 +要強設所有權嗎? - Unable to contentify: -No parcel selected. + 無法進行「內容化」: +未選擇地段。 - Unable to contentify: -No region selected. + 無法進行「內容化」: +未選擇區域。 無法放棄土地: -無地段被選擇。 +未選擇地段。 無法放棄土地: -無法尋找地區。 +找不到地區。 無法購買土地: -無地段被選擇。 +未選擇地段。 - Unable to buy land: -Cannot find the region this land is in. + 無法購買土地: +找不到這土地所在的地區。 - You cannot close the Buy Land window until [APP_NAME] estimates the price of this transaction. + 必須等待 [APP_NAME] 估計此交易的價格後,你才能關閉「購買土地」視窗。 無法讓渡土地: -無地段被選取。 +未選擇地段。 無法讓渡土地: @@ -786,7 +786,7 @@ Cannot find the region this land is in. 無法讓渡土地: -無法發現這塊土地所在的地區。 +找不到這土地所在的地區。 無法讓渡土地: @@ -802,7 +802,7 @@ Cannot find the region this land is in. 無法讓渡土地: -此 [REGION] 地區並不允許土地轉移。 +「[REGION]」地區不允許土地轉移。 無法放棄土地: @@ -823,11 +823,11 @@ Cannot find the region this land is in. 無法放棄土地: -無法發現這快土地所在的地區。 +找不到這土地所在的地區。 無法放棄土地: -此 [REGION] 地區並不許土地轉移。 +「[REGION]」地區不允許土地轉移。 無法放棄土地: @@ -845,7 +845,7 @@ Cannot find the region this land is in. 無法分割土地: -無地段被選取。 +未選擇地段。 無法分割土地: @@ -854,22 +854,22 @@ Cannot find the region this land is in. 請試著選取其中部分地段。 - 分割土地會將佌地段一分為二,每一個地段將都各自有其設定。 在這個操作之後,一些設定值將會回復到預設值。 + 分割土地將把地段分為兩份,每個新地段有各自的設定。 完成後,部分設定將重設為預設值。 進行分割土地? 無法分割土地: -無法發現這塊土地所在的地區。 +找不到這土地所在的地區。 無法合併土地: -無法發現這塊土地所在的地區。 +找不到這土地所在的地區。 無法合併土地: -無地段被選取。 +未選擇地段。 無法合併土地: @@ -891,42 +891,42 @@ Cannot find the region this land is in. - 在物品能被覆製或察看前,這記事卡必須先進行儲存。儲存記事卡? + 覆製或察看前,這記事卡必須先進行儲存。 儲存記事卡? 覆製這個物品到你的收納區? - + - Failed to switch resolution to [RESX] by [RESY] + 無法將解析度調為 [RESX] X [RESY] - Error: Undefined grasses: [SPECIES] + 錯誤:未定義的草種:[SPECIES] - Error: Undefined trees: [SPECIES] + 錯誤:未定義的樹種:[SPECIES] - Unable to save '[NAME]' to wearable file. You will need to free up some space on your computer and save the wearable again. + 無法將「[NAME]」儲存為可穿裝扮檔案。 你需要空出一些電腦空間,再試著儲存可穿裝扮。 - Unable to save [NAME] to central asset store. -This is usually a temporary failure. Please customize and save the wearable again in a few minutes. + 無法將「[NAME]」儲存到中央資產儲庫。 +這異常狀況通常只是暫時的。 請稍待幾分鐘後,再自訂並儲存可穿裝扮。 - Darn. You have been logged out of [SECOND_LIFE] + 糟糕! 你已被登出 [SECOND_LIFE] [MESSAGE] - + - Unable to buy land for the group: -You do not have permission to buy land for your active group. + 無法為這群組購買土地: +你沒有權限為你當前的群組購買土地。 - Friends can give permissions to track each other on the map and receive online status updates. + 朋友可以允許彼此在地圖上追蹤對方,並接收彼此的線上狀態更新訊息。 -Offer friendship to [NAME]? +向 [NAME] 發出交友邀請?
你願意成為我的朋友嗎? @@ -939,17 +939,17 @@ Offer friendship to [NAME]? 儲存我正在穿的為新裝扮: - [DESC] (新) + [DESC](新)
@@ -38,6 +39,7 @@ + @@ -123,6 +125,11 @@ + + + + + @@ -173,7 +180,6 @@ - @@ -376,7 +382,6 @@ - diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index a5923526e5..33dfcdcc84 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -37,6 +37,12 @@