From 69ca6cd5b227e87e15408d1908c353cc2005aea5 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 27 Jun 2012 12:57:07 +0200 Subject: [PATCH 001/123] WIP: Make curl thread code robust and flexible. Conflicts: indra/llmessage/llcurl.cpp indra/llmessage/llcurl.h indra/newview/app_settings/settings.xml indra/newview/llappviewer.cpp indra/newview/llmeshrepository.cpp Resolved: indra/llmessage/llcurl.cpp: Basically removed (not used anyway) indra/llmessage/llcurl.h: Basically removed (just includes aiculr.h now) indra/newview/app_settings/settings.xml: CurlUseMultipleThreads was remvoved. CurlMaximumNumberOfHandles and CurlRequestTimeOut are still in there, but unused at the moment. indra/newview/llappviewer.cpp: CurlMaximumNumberOfHandles and CurlRequestTimeOut are unused at the moment. indra/newview/llmeshrepository.cpp: Lock mSignal always (is unlocked inside wait()). Use mSignal lock to see if we are waiting; remove mWaiting. Return false from the MeshFetch functions iff we have to retry a HTTP fetch. Catch the error exception thrown by getByteRange instead of using it's return value (always returns true anyway). --- indra/cwdebug/debug.cc | 1 + indra/cwdebug/debug.h | 1 + indra/llcommon/llapp.cpp | 14 + indra/llcommon/llapp.h | 4 + indra/llcommon/llerrorthread.cpp | 6 +- indra/llcommon/llqueuedthread.h | 3 +- indra/llcommon/llthread.cpp | 16 +- indra/llcommon/llthread.h | 36 +- indra/llmessage/CMakeLists.txt | 6 +- indra/llmessage/aicurl.cpp | 1137 +++++++++++++ indra/llmessage/aicurl.h | 317 ++++ indra/llmessage/aicurlprivate.h | 360 ++++ indra/llmessage/aicurlthread.cpp | 1054 ++++++++++++ indra/llmessage/aicurlthread.h | 185 ++ indra/llmessage/llcurl.cpp | 1504 +---------------- indra/llmessage/llcurl.h | 476 +----- indra/llmessage/llhttpclient.cpp | 193 +-- indra/llmessage/llhttpclient.h | 4 +- indra/llmessage/llproxy.cpp | 24 +- indra/llmessage/llproxy.h | 14 +- indra/llmessage/llsdmessage.cpp | 4 +- indra/llmessage/llsdrpcclient.h | 22 +- indra/llmessage/llurlrequest.cpp | 239 +-- indra/llmessage/llurlrequest.h | 11 +- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 11 - indra/newview/hipporestrequest.cpp | 16 +- indra/newview/llagent.cpp | 2 +- indra/newview/llagentlanguage.cpp | 2 +- indra/newview/llappviewer.cpp | 18 +- indra/newview/llcurlrequest.cpp | 147 ++ indra/newview/llcurlrequest.h | 59 + indra/newview/llfloaterregioninfo.cpp | 2 +- indra/newview/llmeshrepository.cpp | 213 ++- indra/newview/llmeshrepository.h | 8 +- indra/newview/llpanelclassified.cpp | 2 +- indra/newview/llstartup.cpp | 8 +- indra/newview/lltexturefetch.cpp | 13 +- indra/newview/lltexturefetch.h | 6 +- indra/newview/llviewerparcelmedia.cpp | 2 +- indra/newview/llviewerparcelmgr.cpp | 2 +- indra/newview/llxmlrpctransaction.cpp | 273 ++- indra/newview/llxmlrpctransaction.h | 4 +- indra/newview/statemachine/CMakeLists.txt | 4 +- .../aicurleasyrequeststatemachine.cpp | 152 ++ .../aicurleasyrequeststatemachine.h | 96 ++ indra/newview/statemachine/aistatemachine.h | 2 +- 47 files changed, 4173 insertions(+), 2502 deletions(-) create mode 100644 indra/llmessage/aicurl.cpp create mode 100644 indra/llmessage/aicurl.h create mode 100644 indra/llmessage/aicurlprivate.h create mode 100644 indra/llmessage/aicurlthread.cpp create mode 100644 indra/llmessage/aicurlthread.h create mode 100644 indra/newview/llcurlrequest.cpp create mode 100644 indra/newview/llcurlrequest.h create mode 100644 indra/newview/statemachine/aicurleasyrequeststatemachine.cpp create mode 100644 indra/newview/statemachine/aicurleasyrequeststatemachine.h diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 2a4f1c4fa..bd7d712f4 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -173,6 +173,7 @@ void stop_recording_backtraces(void) channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine. channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities. + channel_ct curl DDCN("CURL"); //!< This debug channel is used for output related to Curl. } // namespace dc } // namespace DEBUGCHANNELS diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 786b0c52a..10750f6ab 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -118,6 +118,7 @@ extern CWD_API channel_ct sdl; extern CWD_API channel_ct backtrace; extern CWD_API channel_ct statemachine; extern CWD_API channel_ct caps; +extern CWD_API channel_ct curl; #endif diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index cb2b3c0ca..316b07e83 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -297,6 +297,20 @@ void LLApp::startErrorThread() } } +void LLApp::stopErrorThread() +{ + LLApp::setStopped(); // Signal error thread that we stopped. + int count = 0; + while (mThreadErrorp && !mThreadErrorp->isStopped() && ++count < 100) + { + ms_sleep(10); + } + if (mThreadErrorp && !mThreadErrorp->isStopped()) + { + llwarns << "Failed to stop Error Thread." << llendl; + } +} + void LLApp::setErrorHandler(LLAppErrorHandler handler) { LLApp::sErrorHandler = handler; diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 6ad34c8fa..072f12005 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -260,6 +260,10 @@ protected: * @ brief This method is called once as soon as logging is initialized. */ void startErrorThread(); + /** + * @brief This method is called at the end, just prior to deinitializing curl. + */ + void stopErrorThread(); private: void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions) diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp index 8476d0bfe..da6f35089 100644 --- a/indra/llcommon/llerrorthread.cpp +++ b/indra/llcommon/llerrorthread.cpp @@ -194,17 +194,17 @@ void LLErrorThread::run() if (LLApp::isError()) { // The app is in an error state, run the application's error handler. - //llinfos << "thread_error - An error has occurred, running error callback!" << llendl; + Dout(dc::notice, "thread_error - An error has occurred, running error callback!"); // Run the error handling callback LLApp::runErrorHandler(); } else { // Everything is okay, a clean exit. - //llinfos << "thread_error - Application exited cleanly" << llendl; + Dout(dc::notice, "thread_error - Application exited cleanly"); } - //llinfos << "thread_error - Exiting" << llendl; + Dout(dc::notice, "thread_error - Exiting"); LLApp::sErrorThreadRunning = FALSE; } diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 2680072bc..73c7d98ee 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -110,6 +110,8 @@ public: return mPriority > second.mPriority; } + virtual void deleteRequest(); // Only method to delete a request + protected: status_t setStatus(status_t newstatus) { @@ -125,7 +127,6 @@ public: virtual bool processRequest() = 0; // Return true when request has completed virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted - virtual void deleteRequest(); // Only method to delete a request void setPriority(U32 pri) { diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 6f07b5482..d1c02237f 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -68,6 +68,7 @@ U32 ll_thread_local local_thread_ID = 0; U32 LLThread::sIDIter = 0; LLAtomicS32 LLThread::sCount = 0; +LLAtomicS32 LLThread::sRunning = 0; LL_COMMON_API void assert_main_thread() { @@ -119,6 +120,7 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap // the critical area of the mSignal lock)]. lldebugs << "LLThread::staticRun() Exiting: " << name << llendl; + --sRunning; // Would be better to do this after joining with the thread, but we don't join :/ return NULL; } @@ -194,6 +196,7 @@ void LLThread::start() // Set thread state to running mStatus = RUNNING; + sRunning++; apr_status_t status = apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool()); @@ -205,6 +208,7 @@ void LLThread::start() } else { + --sRunning; mStatus = STOPPED; llwarns << "failed to start thread " << mName << llendl; ll_apr_warn_status(status); @@ -315,6 +319,16 @@ LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread // The thread private handle to access the LLThreadLocalData instance. apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey; +LLThreadLocalData::LLThreadLocalData(char const* name) : mCurlMultiHandle(NULL), mCurlErrorBuffer(NULL), mName(name) +{ +} + +LLThreadLocalData::~LLThreadLocalData() +{ + delete mCurlMultiHandle; + delete [] mCurlErrorBuffer; +} + //static void LLThreadLocalData::init(void) { @@ -348,7 +362,7 @@ void LLThreadLocalData::destroy(void* thread_local_data) //static void LLThreadLocalData::create(LLThread* threadp) { - LLThreadLocalData* new_tld = new LLThreadLocalData; + LLThreadLocalData* new_tld = new LLThreadLocalData(threadp ? threadp->mName.c_str() : "main thread"); if (threadp) { threadp->mThreadLocalData = new_tld; diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 5724e0087..b6287bb29 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -57,6 +57,12 @@ class LLCondition; #define ll_thread_local __thread #endif +class LL_COMMON_API LLThreadLocalDataMember +{ +public: + virtual ~LLThreadLocalDataMember() { }; +}; + class LL_COMMON_API LLThreadLocalData { private: @@ -66,11 +72,18 @@ public: // Thread-local memory pool. LLAPRRootPool mRootPool; LLVolatileAPRPool mVolatileAPRPool; + LLThreadLocalDataMember* mCurlMultiHandle; // Initialized by AICurlMultiHandle::getInstance + char* mCurlErrorBuffer; // NULL, or pointing to a buffer used by libcurl. + std::string mName; // "main thread", or a copy of LLThread::mName. static void init(void); static void destroy(void* thread_local_data); static void create(LLThread* pthread); static LLThreadLocalData& tldata(void); + +private: + LLThreadLocalData(char const* name); + ~LLThreadLocalData(); }; class LL_COMMON_API LLThread @@ -78,6 +91,7 @@ class LL_COMMON_API LLThread private: static U32 sIDIter; static LLAtomicS32 sCount; + static LLAtomicS32 sRunning; public: typedef enum e_thread_status @@ -96,8 +110,9 @@ public: static U32 currentID(); // Return ID of current thread static S32 getCount() { return sCount; } + static S32 getRunning() { return sRunning; } static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. - + public: // PAUSE / RESUME functionality. See source code for important usage notes. // Called from MAIN THREAD. @@ -204,6 +219,11 @@ protected: apr_thread_mutex_t* mAPRMutexp; mutable U32 mCount; mutable U32 mLockingThread; + +private: + // Disallow copy construction and assignment. + LLMutexBase(LLMutexBase const&); + LLMutexBase& operator=(LLMutexBase const&); }; class LL_COMMON_API LLMutex : public LLMutexBase @@ -223,10 +243,6 @@ public: protected: LLAPRPool mPool; -private: - // Disable copy construction, as si teh bomb!!! -SG - LLMutex(const LLMutex&); - LLMutex& operator=(const LLMutex&); }; #if APR_HAS_THREADS @@ -380,6 +396,16 @@ public: mNoHoldersCondition.signal(); // Tell waiting readers, see [5]. mNoHoldersCondition.unlock(); // Release lock on mHoldersCount. } +#if LL_DEBUG + // Really only intended for debugging purposes: + bool isLocked(void) + { + mNoHoldersCondition.lock(); + bool res = mHoldersCount; + mNoHoldersCondition.unlock(); + return res; + } +#endif }; //============================================================================ diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index b09245ba3..fbf872c7f 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -29,7 +29,8 @@ set(llmessage_SOURCE_FILES llchainio.cpp llcircuit.cpp llclassifiedflags.cpp - llcurl.cpp + aicurl.cpp + aicurlthread.cpp lldatapacker.cpp lldispatcher.cpp llfiltersd2xmlrpc.cpp @@ -117,6 +118,9 @@ set(llmessage_HEADER_FILES llcircuit.h llclassifiedflags.h llcurl.h + aicurl.h + aicurlprivate.h + aicurlthread.h lldatapacker.h lldbstrings.h lldispatcher.h diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp new file mode 100644 index 000000000..6278c91c2 --- /dev/null +++ b/indra/llmessage/aicurl.cpp @@ -0,0 +1,1137 @@ +/** + * @file aicurl.cpp + * @brief Implementation of AICurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * Copyright (C) 2010, Linden Research, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 20/03/2012 + * Added copyright notice for Linden Lab for those parts that were + * copied or derived from llcurl.cpp. The code of those parts are + * already in their own llcurl.cpp, so they do not ever need to + * even look at this file; the reason I added the copyright notice + * is to make clear that I am not the author of 100% of this code + * and hence I cannot change the license of it. + */ + +#include "linden_common.h" + +#define OPENSSL_THREAD_DEFINES +#include // OPENSSL_THREADS +#include + +#include "aicurl.h" +#include "llbufferstream.h" +#include "llsdserialize.h" +#include "aithreadsafe.h" +#include "llqueuedthread.h" +#include "lltimer.h" // ms_sleep +#include "llproxy.h" +#include "llhttpstatuscodes.h" +#ifdef CWDEBUG +#include +#endif + +//================================================================================== +// Local variables. +// + +namespace { + +struct CertificateAuthority { + std::string file; + std::string path; +}; + +AIThreadSafeSimpleDC gCertificateAuthority; +typedef AIAccess CertificateAuthority_wat; +typedef AIAccessConst CertificateAuthority_rat; + +enum gSSLlib_type { + ssl_unknown, + ssl_openssl, + ssl_gnutls, + ssl_nss +}; + +// No locking needed: initialized before threads are created, and subsequently only read. +gSSLlib_type gSSLlib; +bool gSetoptParamsNeedDup; + +} // namespace + +//----------------------------------------------------------------------------------- +// Needed for thread-safe openSSL operation. + +// Must be defined in global namespace. +struct CRYPTO_dynlock_value +{ + AIRWLock rwlock; +}; + +namespace { + +AIRWLock* ssl_rwlock_array; + +// OpenSSL locking function. +void ssl_locking_function(int mode, int n, char const* file, int line) +{ + if ((mode & CRYPTO_LOCK)) + { + if ((mode & CRYPTO_READ)) + ssl_rwlock_array[n].rdlock(); + else + ssl_rwlock_array[n].wrlock(); + } + else + { + if ((mode & CRYPTO_READ)) + ssl_rwlock_array[n].rdunlock(); + else + ssl_rwlock_array[n].wrunlock(); + } +} + +// OpenSSL uniq id function. +void ssl_id_function(CRYPTO_THREADID* thread_id) +{ +#if 1 // apr_os_thread_current() returns an unsigned long. + CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); +#else // if it would return a pointer. + CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); +#endif +} + +// OpenSSL allocate and initialize dynamic crypto lock. +CRYPTO_dynlock_value* ssl_dyn_create_function(char const* file, int line) +{ + return new CRYPTO_dynlock_value; +} + +// OpenSSL destroy dynamic crypto lock. +void ssl_dyn_destroy_function(CRYPTO_dynlock_value* l, char const* file, int line) +{ + delete l; +} + +// OpenSSL dynamic locking function. +void ssl_dyn_lock_function(int mode, CRYPTO_dynlock_value* l, char const* file, int line) +{ + if ((mode & CRYPTO_LOCK)) + { + if ((mode & CRYPTO_READ)) + l->rwlock.rdlock(); + else + l->rwlock.wrlock(); + } + else + { + if ((mode & CRYPTO_READ)) + l->rwlock.rdunlock(); + else + l->rwlock.wrunlock(); + } +} + +typedef void (*ssl_locking_function_type)(int, int, char const*, int); +typedef void (*ssl_id_function_type)(CRYPTO_THREADID*); +typedef CRYPTO_dynlock_value* (*ssl_dyn_create_function_type)(char const*, int); +typedef void (*ssl_dyn_destroy_function_type)(CRYPTO_dynlock_value*, char const*, int); +typedef void (*ssl_dyn_lock_function_type)(int, CRYPTO_dynlock_value*, char const*, int); + +ssl_locking_function_type old_ssl_locking_function; +ssl_id_function_type old_ssl_id_function; +ssl_dyn_create_function_type old_ssl_dyn_create_function; +ssl_dyn_destroy_function_type old_ssl_dyn_destroy_function; +ssl_dyn_lock_function_type old_ssl_dyn_lock_function; + +// Initialize OpenSSL library for thread-safety. +void ssl_init(void) +{ + // Static locks vector. + ssl_rwlock_array = new AIRWLock[CRYPTO_num_locks()]; + // Static locks callbacks. + old_ssl_locking_function = CRYPTO_get_locking_callback(); + old_ssl_id_function = CRYPTO_THREADID_get_callback(); + CRYPTO_set_locking_callback(&ssl_locking_function); + CRYPTO_THREADID_set_callback(&ssl_id_function); // Setting this avoids the need for a thread-local error number facility, which is hard to check. + // Dynamic locks callbacks. + old_ssl_dyn_create_function = CRYPTO_get_dynlock_create_callback(); + old_ssl_dyn_lock_function = CRYPTO_get_dynlock_lock_callback(); + old_ssl_dyn_destroy_function = CRYPTO_get_dynlock_destroy_callback(); + CRYPTO_set_dynlock_create_callback(&ssl_dyn_create_function); + CRYPTO_set_dynlock_lock_callback(&ssl_dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); +} + +// Cleanup OpenSLL library thread-safety. +void ssl_cleanup(void) +{ + // Dynamic locks callbacks. + CRYPTO_set_dynlock_destroy_callback(old_ssl_dyn_destroy_function); + CRYPTO_set_dynlock_lock_callback(old_ssl_dyn_lock_function); + CRYPTO_set_dynlock_create_callback(old_ssl_dyn_create_function); + // Static locks callbacks. + CRYPTO_THREADID_set_callback(old_ssl_id_function); + CRYPTO_set_locking_callback(old_ssl_locking_function); + // Static locks vector. + delete [] ssl_rwlock_array; +} + +} // namespace openSSL +//----------------------------------------------------------------------------------- + + +//================================================================================== +// External API +// + +namespace AICurlInterface { + +#undef AICurlPrivate +using AICurlPrivate::check_easy_code; + +// MAIN-THREAD +void initCurl(F32 curl_request_timeout, S32 max_number_handles) +{ + DoutEntering(dc::curl, "AICurlInterface::initCurl(" << curl_request_timeout << ", " << max_number_handles << ")"); + + llassert(LLThread::getRunning() == 0); // We must not call curl_global_init unless we are the only thread. + CURLcode res = check_easy_code(curl_global_init(CURL_GLOBAL_ALL)); + if (res != CURLE_OK) + { + llerrs << "curl_global_init(CURL_GLOBAL_ALL) failed." << llendl; + } + + // Print version and do some feature sanity checks. + { + curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); + + llassert_always(version_info->age >= 0); + if (version_info->age < 1) + { + llwarns << "libcurl's age is 0; no ares support." << llendl; + } + llassert_always((version_info->features & CURL_VERSION_SSL)); // SSL support, added in libcurl 7.10. + if (!(version_info->features & CURL_VERSION_ASYNCHDNS)); // Asynchronous name lookups (added in libcurl 7.10.7). + { + llwarns << "libcurl was not compiled with support for asynchronous name lookups!" << llendl; + } + + llinfos << "Successful initialization of libcurl " << + version_info->version << " (" << version_info->version_num << "), (" << + version_info->ssl_version << ", libz/" << version_info->libz_version << ")." << llendl; + + // Detect SSL library used. + gSSLlib = ssl_unknown; + std::string ssl_version(version_info->ssl_version); + if (ssl_version.find("OpenSSL") != std::string::npos) + gSSLlib = ssl_openssl; // See http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION + else if (ssl_version.find("GnuTLS") != std::string::npos) + gSSLlib = ssl_gnutls; // See http://www.gnu.org/software/gnutls/manual/html_node/Thread-safety.html + else if (ssl_version.find("NSS") != std::string::npos) + gSSLlib = ssl_nss; // Supposedly thread-safe without any requirements. + + // Set up thread-safety requirements of underlaying SSL library. + // See http://curl.haxx.se/libcurl/c/libcurl-tutorial.html + switch (gSSLlib) + { + case ssl_unknown: + { + llerrs << "Unknown SSL library \"" << version_info->ssl_version << "\", required actions for thread-safe handling are unknown! Bailing out." << llendl; + } + case ssl_openssl: + { +#ifndef OPENSSL_THREADS + llerrs << "OpenSSL was not configured with thread support! Bailing out." << llendl; +#endif + ssl_init(); + } + case ssl_gnutls: + { + // I don't think we ever get here, do we? --Aleric + llassert_always(gSSLlib != ssl_gnutls); + // If we do, then didn't curl_global_init already call gnutls_global_init? + // It seems there is nothing to do for us here. + } + case ssl_nss: + { + break; // No requirements. + } + } + + gSetoptParamsNeedDup = (version_info->version_num < 0x071700); + if (gSetoptParamsNeedDup) + { + llwarns << "Your libcurl version is too old." << llendl; + } + llassert_always(!gSetoptParamsNeedDup); // Might add support later. + } +} + +// MAIN-THREAD +void cleanupCurl(void) +{ + using AICurlPrivate::curlThreadIsRunning; + + DoutEntering(dc::curl, "AICurlInterface::cleanupCurl()"); + + ssl_cleanup(); + + llassert(LLThread::getRunning() <= (curlThreadIsRunning() ? 1 : 0)); // We must not call curl_global_cleanup unless we are the only thread left. + curl_global_cleanup(); +} + +// THREAD-SAFE +std::string getVersionString(void) +{ + // libcurl is thread safe, no locking needed. + return curl_version(); +} + +// THREAD-SAFE +void setCAFile(std::string const& file) +{ + CertificateAuthority_wat CertificateAuthority_w(gCertificateAuthority); + CertificateAuthority_w->file = file; +} + +// This function is not called from anywhere, but provided as part of AICurlInterface because setCAFile is. +// THREAD-SAFE +void setCAPath(std::string const& path) +{ + CertificateAuthority_wat CertificateAuthority_w(gCertificateAuthority); + CertificateAuthority_w->path = path; +} + +// THREAD-SAFE +std::string strerror(CURLcode errorcode) +{ + // libcurl is thread safe, no locking needed. + return curl_easy_strerror(errorcode); +} + +//----------------------------------------------------------------------------- +// class Responder +// + +Responder::Responder(void) : mReferenceCount(0) +{ + DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this); +} + +Responder::~Responder() +{ + DoutEntering(dc::curl, "AICurlInterface::Responder::~Responder() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount); + llassert(mReferenceCount == 0); +} + +void Responder::setURL(std::string const& url) +{ + // setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course). + // We don't need locking here therefore; it's a case of initializing before use. + mURL = url; +} + +// Called with HTML header. +// virtual +void Responder::completedHeader(U32, std::string const&, LLSD const&) +{ + // Nothing. +} + +// Called with HTML body. +// virtual +void Responder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer) +{ + LLSD content; + LLBufferStream istr(channels, buffer.get()); + if (!LLSDSerialize::fromXML(content, istr)) + { + llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; + } + + // Allow derived class to override at this point. + completed(status, reason, content); +} + +void Responder::fatalError(std::string const& reason) +{ + llwarns << "Responder::fatalError(\"" << reason << "\") is called (" << mURL << "). Passing it to Responder::completed with fake HTML error status and empty HTML body!" << llendl; + completed(U32_MAX, reason, LLSD()); +} + +// virtual +void Responder::completed(U32 status, std::string const& reason, LLSD const& content) +{ + // HTML status good? + if (200 <= status && status < 300) + { + // Allow derived class to override at this point. + result(content); + } + else + { + // Allow derived class to override at this point. + errorWithContent(status, reason, content); + } +} + +// virtual +void Responder::errorWithContent(U32 status, std::string const& reason, LLSD const&) +{ + // Allow derived class to override at this point. + error(status, reason); +} + +// virtual +void Responder::error(U32 status, std::string const& reason) +{ + llinfos << mURL << " [" << status << "]: " << reason << llendl; +} + +// virtual +void Responder::result(LLSD const&) +{ + // Nothing. +} + +// Friend functions. + +void intrusive_ptr_add_ref(Responder* responder) +{ + responder->mReferenceCount++; +} + +void intrusive_ptr_release(Responder* responder) +{ + if (--responder->mReferenceCount == 0) + { + delete responder; + } +} + +} // namespace AICurlInterface +//================================================================================== + + +//================================================================================== +// Local implementation. +// + +namespace AICurlPrivate { + +// THREAD-SAFE +CURLcode check_easy_code(CURLcode code) +{ + if (code != CURLE_OK) + { + char* error_buffer = LLThreadLocalData::tldata().mCurlErrorBuffer; + llinfos << "curl easy error detected: " << curl_easy_strerror(code); + if (error_buffer && *error_buffer != '\0') + { + llcont << ": " << error_buffer; + } + llcont << llendl; + } + return code; +} + +// THREAD-SAFE +CURLMcode check_multi_code(CURLMcode code) +{ + if (code != CURLM_OK) + { + llinfos << "curl multi error detected: " << curl_multi_strerror(code) << llendl; + } + return code; +} + +//============================================================================= +// AICurlEasyRequest (base classes) +// + +//----------------------------------------------------------------------------- +// CurlEasyHandle + +LLAtomicU32 CurlEasyHandle::sTotalEasyHandles; + +// Throws AICurlNoEasyHandle. +CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) +{ + mEasyHandle = curl_easy_init(); +#if 0 + //FIXME: for debugging, throw once every 10 times. + static int c = 0; + if (++c % 10 == 5) + { + curl_easy_cleanup(mEasyHandle); + mEasyHandle = NULL; + } +#endif + if (!mEasyHandle) + { + throw AICurlNoEasyHandle("curl_easy_init() returned NULL"); + } + sTotalEasyHandles++; +} + +CurlEasyHandle::~CurlEasyHandle() +{ + llassert(!mActiveMultiHandle); + curl_easy_cleanup(mEasyHandle); + --sTotalEasyHandles; +} + +//static +char* CurlEasyHandle::getTLErrorBuffer(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + if (!tldata.mCurlErrorBuffer) + { + tldata.mCurlErrorBuffer = new char[CURL_ERROR_SIZE]; + } + return tldata.mCurlErrorBuffer; +} + +void CurlEasyHandle::setErrorBuffer(void) +{ + char* error_buffer = getTLErrorBuffer(); + if (mErrorBuffer != error_buffer) + { + curl_easy_setopt(mEasyHandle, CURLOPT_ERRORBUFFER, error_buffer); + mErrorBuffer = error_buffer; + } +} + +CURLcode CurlEasyHandle::getinfo(CURLINFO info, void* data) +{ + setErrorBuffer(); + return check_easy_code(curl_easy_getinfo(mEasyHandle, info, data)); +} + +char* CurlEasyHandle::escape(char* url, int length) +{ + return curl_easy_escape(mEasyHandle, url, length); +} + +char* CurlEasyHandle::unescape(char* url, int inlength , int* outlength) +{ + return curl_easy_unescape(mEasyHandle, url, inlength, outlength); +} + +CURLcode CurlEasyHandle::perform(void) +{ + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_perform(mEasyHandle)); +} + +CURLcode CurlEasyHandle::pause(int bitmask) +{ + setErrorBuffer(); + return check_easy_code(curl_easy_pause(mEasyHandle, bitmask)); +} + +CURLMcode CurlEasyHandle::add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi) +{ + llassert_always(!mActiveMultiHandle && multi); + mActiveMultiHandle = multi; + CURLMcode res = check_multi_code(curl_multi_add_handle(multi, mEasyHandle)); + added_to_multi_handle(curl_easy_request_w); + return res; +} + +CURLMcode CurlEasyHandle::remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi) +{ + llassert_always(mActiveMultiHandle && mActiveMultiHandle == multi); + mActiveMultiHandle = NULL; + CURLMcode res = check_multi_code(curl_multi_remove_handle(multi, mEasyHandle)); + removed_from_multi_handle(curl_easy_request_w); + return res; +} + +void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request) +{ + threadsafe_curl_easy_request->mReferenceCount++; +} + +void intrusive_ptr_release(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_request) +{ + if (--threadsafe_curl_easy_request->mReferenceCount == 0) + { + delete threadsafe_curl_easy_request; + } +} + +//----------------------------------------------------------------------------- +// CurlEasyReqest + +void CurlEasyRequest::setoptString(CURLoption option, std::string const& value) +{ + llassert(!gSetoptParamsNeedDup); + setopt(option, value.c_str()); +} + +void CurlEasyRequest::setPost(char const* postdata, S32 size) +{ + setopt(CURLOPT_POST, 1L); + setopt(CURLOPT_POSTFIELDS, static_cast(const_cast(postdata))); + setopt(CURLOPT_POSTFIELDSIZE, size); +} + +ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void) +{ + return static_cast(AIThreadSafeSimpleDC::wrapper_cast(this)); +} + +//static +size_t CurlEasyRequest::headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mHeaderCallback(ptr, size, nmemb, self->mHeaderCallbackUserData); +} + +void CurlEasyRequest::setHeaderCallback(curl_write_callback callback, void* userdata) +{ + mHeaderCallback = callback; + mHeaderCallbackUserData = userdata; + setopt(CURLOPT_HEADERFUNCTION, callback ? &CurlEasyRequest::headerCallback : NULL); + setopt(CURLOPT_WRITEHEADER, userdata ? this : NULL); +} + +//static +size_t CurlEasyRequest::writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mWriteCallback(ptr, size, nmemb, self->mWriteCallbackUserData); +} + +void CurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) +{ + mWriteCallback = callback; + mWriteCallbackUserData = userdata; + setopt(CURLOPT_WRITEFUNCTION, callback ? &CurlEasyRequest::writeCallback : NULL); + setopt(CURLOPT_WRITEDATA, userdata ? this : NULL); +} + +//static +size_t CurlEasyRequest::readCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mReadCallback(ptr, size, nmemb, self->mReadCallbackUserData); +} + +void CurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) +{ + mReadCallback = callback; + mReadCallbackUserData = userdata; + setopt(CURLOPT_READFUNCTION, callback ? &CurlEasyRequest::readCallback : NULL); + setopt(CURLOPT_READDATA, userdata ? this : NULL); +} + +//static +CURLcode CurlEasyRequest::SSLCtxCallback(CURL* curl, void* sslctx, void* userdata) +{ + CurlEasyRequest* self = static_cast(userdata); + ThreadSafeCurlEasyRequest* lockobj = self->get_lockobj(); + AICurlEasyRequest_wat lock_self(*lockobj); + return self->mSSLCtxCallback(curl, sslctx, self->mSSLCtxCallbackUserData); +} + +void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ + mSSLCtxCallback = callback; + mSSLCtxCallbackUserData = userdata; + setopt(CURLOPT_SSL_CTX_FUNCTION, callback ? &CurlEasyRequest::SSLCtxCallback : NULL); + setopt(CURLOPT_SSL_CTX_DATA, userdata ? this : NULL); +} + +static size_t noHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llwarns << "Calling noHeaderCallback(); curl session aborted." << llendl; + return 0; // Cause a CURL_WRITE_ERROR +} + +static size_t noWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llwarns << "Calling noWriteCallback(); curl session aborted." << llendl; + return 0; // Cause a CURL_WRITE_ERROR +} + +static size_t noReadCallback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + llwarns << "Calling noReadCallback(); curl session aborted." << llendl; + return CURL_READFUNC_ABORT; // Cause a CURLE_ABORTED_BY_CALLBACK +} + +static CURLcode noSSLCtxCallback(CURL* curl, void* sslctx, void* parm) +{ + llwarns << "Calling noSSLCtxCallback(); curl session aborted." << llendl; + return CURLE_ABORTED_BY_CALLBACK; +} + +void CurlEasyRequest::revokeCallbacks(void) +{ + if (mHeaderCallback == &noHeaderCallback && + mWriteCallback == &noWriteCallback && + mReadCallback == &noReadCallback && + mSSLCtxCallback == &noSSLCtxCallback) + { + // Already revoked. + return; + } + mHeaderCallback = &noHeaderCallback; + mWriteCallback = &noWriteCallback; + mReadCallback = &noReadCallback; + mSSLCtxCallback = &noSSLCtxCallback; + if (active()) + { + llwarns << "Revoking callbacks on a still active CurlEasyRequest object!" << llendl; + } + curl_easy_setopt(getEasyHandle(), CURLOPT_HEADERFUNCTION, &noHeaderCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_WRITEHEADER, &noWriteCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_READFUNCTION, &noReadCallback); + curl_easy_setopt(getEasyHandle(), CURLOPT_SSL_CTX_FUNCTION, &noSSLCtxCallback); +} + +CurlEasyRequest::~CurlEasyRequest() +{ + // If the CurlEasyRequest object is destructed then we need to revoke all callbacks, because + // we can't set the lock anymore, and neither will mHeaderCallback, mWriteCallback etc, + // be available anymore. + send_events_to(NULL); + revokeCallbacks(); + // This wasn't freed yet if the request never finished. + curl_slist_free_all(mHeaders); +} + +void CurlEasyRequest::resetState(void) +{ + // This function should not revoke the event call backs! + revokeCallbacks(); + reset(); + curl_slist_free_all(mHeaders); + mHeaders = NULL; + mRequestFinalized = false; + mEventsTarget = NULL; + mResult = CURLE_FAILED_INIT; + applyDefaultOptions(); +} + +void CurlEasyRequest::addHeader(char const* header) +{ + llassert(!mRequestFinalized); + mHeaders = curl_slist_append(mHeaders, header); +} + +#ifdef CWDEBUG +static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr) +{ + using namespace ::libcwd; + + CurlEasyRequest* request = (CurlEasyRequest*)user_ptr; + std::ostringstream marker; + marker << (void*)request->get_lockobj(); + libcw_do.push_marker(); + libcw_do.marker().assign(marker.str().data(), marker.str().size()); + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curl|cond_nonewline_cf(infotype == CURLINFO_TEXT)) + switch (infotype) + { + case CURLINFO_TEXT: + LibcwDoutStream << "* "; + break; + case CURLINFO_HEADER_IN: + LibcwDoutStream << "H> "; + break; + case CURLINFO_HEADER_OUT: + LibcwDoutStream << "H< "; + break; + case CURLINFO_DATA_IN: + LibcwDoutStream << "D> "; + break; + case CURLINFO_DATA_OUT: + LibcwDoutStream << "D< "; + break; + case CURLINFO_SSL_DATA_IN: + LibcwDoutStream << "S> "; + break; + case CURLINFO_SSL_DATA_OUT: + LibcwDoutStream << "S< "; + break; + default: + LibcwDoutStream << "?? "; + } + if (infotype == CURLINFO_TEXT) + LibcwDoutStream.write(buf, size); + else if (infotype == CURLINFO_HEADER_IN || infotype == CURLINFO_HEADER_OUT) + LibcwDoutStream << libcwd::buf2str(buf, size); + else if (infotype == CURLINFO_DATA_IN || infotype == CURLINFO_DATA_OUT) + LibcwDoutStream << size << " bytes: \"" << libcwd::buf2str(buf, size) << '"'; + else + LibcwDoutStream << size << " bytes"; + LibcwDoutScopeEnd; + libcw_do.pop_marker(); + return 0; +} +#endif + +void CurlEasyRequest::applyDefaultOptions(void) +{ + CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); + setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); + // This option forces openssl to use TLS version 1. + // The Linden Lab servers don't support later TLS versions, and libopenssl-1.0.1c has + // a bug were renegotion fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), + // causing the connection to fail completely without this hack. + // For a commandline test of the same, observe the difference between: + // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // and + // openssl s_client -tls1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + setopt(CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1); + setopt(CURLOPT_NOSIGNAL, 1); + // The old code did this for the 'buffered' version, but I think it's nonsense. + //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); + // Set the CURL options for either Socks or HTTP proxy. + LLProxy::getInstance()->applyProxySettings(this); + Debug( + if (dc::curl.is_on()) + { + setopt(CURLOPT_VERBOSE, 1); // Usefull for debugging. + setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_callback); + setopt(CURLOPT_DEBUGDATA, this); + } + ); +} + +void CurlEasyRequest::finalizeRequest(std::string const& url) +{ + llassert(!mRequestFinalized); + mResult = CURLE_FAILED_INIT; // General error code, the final code is written here in MultiHandle::check_run_count when msg is CURLMSG_DONE. + mRequestFinalized = true; + lldebugs << url << llendl; + setopt(CURLOPT_HTTPHEADER, mHeaders); + setoptString(CURLOPT_URL, url); + // The following line is a bit tricky: we store a pointer to the object without increasing it's reference count. + // Of course we could increment the reference count, but that doesn't help much: if then this pointer would + // get "lost" we'd have a memory leak. Either way we must make sure that it is impossible that this pointer + // will be used if the object is deleted [In fact, since this is finalizeRequest() and not addRequest(), + // incrementing the reference counter would be wrong: if addRequest is never called then the object is + // destroyed shortly after and this pointer is never even used.] + // This pointer is used in MultiHandle::check_run_count, which means that addRequest() was called and + // the reference counter was increased and the object is being kept alive, see the comments above + // command_queue in aicurlthread.cpp. In fact, this object survived until MultiHandle::add_easy_request + // was called and is kept alive by MultiHandle::mAddedEasyRequests. The only way to get deleted after + // that is when MultiHandle::remove_easy_request is called, which first removes the easy handle from + // the multi handle. So that it's (hopefully) no longer possible that info_read() in + // MultiHandle::check_run_count returns this easy handle, after the object is destroyed by deleting + // it from MultiHandle::mAddedEasyRequests. + setopt(CURLOPT_PRIVATE, get_lockobj()); +} + +void CurlEasyRequest::getTransferInfo(AICurlInterface::TransferInfo* info) +{ + // Curl explicitly demands a double for these info's. + double size, total_time, speed; + getinfo(CURLINFO_SIZE_DOWNLOAD, &size); + getinfo(CURLINFO_TOTAL_TIME, &total_time); + getinfo(CURLINFO_SPEED_DOWNLOAD, &speed); + // Convert to F64. + info->mSizeDownload = size; + info->mTotalTime = total_time; + info->mSpeedDownload = speed; +} + +void CurlEasyRequest::getResult(CURLcode* result, AICurlInterface::TransferInfo* info) +{ + *result = mResult; + if (info && mResult != CURLE_FAILED_INIT) + { + getTransferInfo(info); + } +} + +void CurlEasyRequest::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->added_to_multi_handle(curl_easy_request_w); +} + +void CurlEasyRequest::finished(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->finished(curl_easy_request_w); +} + +void CurlEasyRequest::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mEventsTarget) + mEventsTarget->removed_from_multi_handle(curl_easy_request_w); +} + +//----------------------------------------------------------------------------- +// CurlResponderBuffer + +static unsigned int const MAX_REDIRECTS = 5; +static S32 const CURL_REQUEST_TIMEOUT = 30; // Seconds per operation. + +LLChannelDescriptors const CurlResponderBuffer::sChannels; + +CurlResponderBuffer::CurlResponderBuffer() +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + AICurlEasyRequest_wat curl_easy_request_w(*lockobj); + curl_easy_request_w->send_events_to(this); +} + +// The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). +// The AIThreadSafeSimple is destructed first (right to left), so when we get here then the +// ThreadSafeCurlEasyRequest base class of ThreadSafeBufferedCurlEasyRequest is still intact and we can create +// and use curl_easy_request_w. +CurlResponderBuffer::~CurlResponderBuffer() +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait till possible callbacks have returned. + curl_easy_request_w->send_events_to(NULL); + curl_easy_request_w->revokeCallbacks(); + if (mResponder) + { + llwarns << "Calling ~CurlResponderBuffer() with active responder!" << llendl; + llassert(false); // Does this ever happen? And if so, what does it mean? + // FIXME: Does this really mean it timed out? + mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput); + mResponder = NULL; + } +} + +void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w) +{ + if (mResponder) + { + llwarns << "Calling CurlResponderBuffer::resetState() for active easy handle!" << llendl; + llassert(false); // Does this ever happen? And if so, what does it mean? + // FIXME: Does this really mean it timed out? + mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput); + mResponder = NULL; + } + + curl_easy_request_w->resetState(); + + mOutput.reset(); + + mInput.str(""); + mInput.clear(); + + mHeaderOutput.str(""); + mHeaderOutput.clear(); +} + +ThreadSafeBufferedCurlEasyRequest* CurlResponderBuffer::get_lockobj(void) +{ + return static_cast(AIThreadSafeSimple::wrapper_cast(this)); +} + +void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w, std::vector const& headers, AICurlInterface::ResponderPtr responder, S32 time_out, bool post) +{ + if (post) + { + curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); + } + + mOutput.reset(new LLBufferArray); + mOutput->setThreaded(true); + + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + curl_easy_request_w->setWriteCallback(&curlWriteCallback, lockobj); + curl_easy_request_w->setReadCallback(&curlReadCallback, lockobj); + curl_easy_request_w->setHeaderCallback(&curlHeaderCallback, lockobj); + + // Allow up to five redirects. + if (responder && responder->followRedir()) + { + curl_easy_request_w->setopt(CURLOPT_FOLLOWLOCATION, 1); + curl_easy_request_w->setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); + } + + curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYPEER, true); + // Don't verify host name so urls with scrubbed host names will work (improves DNS performance). + curl_easy_request_w->setopt(CURLOPT_SSL_VERIFYHOST, 0); + + curl_easy_request_w->setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); + + // Keep responder alive. + mResponder = responder; + + if (!post) + { + curl_easy_request_w->addHeader("Connection: keep-alive"); + curl_easy_request_w->addHeader("Keep-alive: 300"); + // Add other headers. + for (std::vector::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) + { + curl_easy_request_w->addHeader((*iter).c_str()); + } + } +} + +//static +size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simulaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + AICurlResponderBuffer_wat buffer_w(*lockobj); + S32 n = size * nmemb; + buffer_w->getOutput()->append(sChannels.in(), (U8 const*)data, n); + return n; +} + +//static +size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simulaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + AICurlResponderBuffer_wat buffer_w(*lockobj); + S32 n = size * nmemb; + S32 startpos = buffer_w->getInput().tellg(); + buffer_w->getInput().seekg(0, std::ios::end); + S32 endpos = buffer_w->getInput().tellg(); + buffer_w->getInput().seekg(startpos, std::ios::beg); + S32 maxn = endpos - startpos; + n = llmin(n, maxn); + buffer_w->getInput().read(data, n); + return n; +} + +//static +size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data) +{ + ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); + + // We need to lock the curl easy request object too, because that lock is used + // to make sure that callbacks and destruction aren't done simulaneously. + AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + + AICurlResponderBuffer_wat buffer_w(*lockobj); + size_t n = size * nmemb; + buffer_w->getHeaderOutput().write(data, n); + return n; +} + +void CurlResponderBuffer::added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::added_to_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); +} + +void CurlResponderBuffer::finished(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::finished(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); +} + +void CurlResponderBuffer::removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) +{ + Dout(dc::curl, "Calling CurlResponderBuffer::removed_from_multi_handle(@" << (void*)&*curl_easy_request_w << ") for this = " << (void*)this); + + // Lock self. + ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); + llassert(dynamic_cast(static_cast(ThreadSafeCurlEasyRequest::wrapper_cast(&*curl_easy_request_w))) == lockobj); + AICurlResponderBuffer_wat buffer_w(*lockobj); + llassert(&*buffer_w == this); + + processOutput(curl_easy_request_w); +} + +void CurlResponderBuffer::processOutput(AICurlEasyRequest_wat& curl_easy_request_w) +{ + U32 responseCode = 0; + std::string responseReason; + + CURLcode code; + curl_easy_request_w->getResult(&code); + if (code == CURLE_OK) + { + curl_easy_request_w->getinfo(CURLINFO_RESPONSE_CODE, &responseCode); + //*TODO: get reason from first line of mHeaderOutput + } + else + { + responseCode = 499; + responseReason = AICurlInterface::strerror(code) + " : "; + if (code == CURLE_FAILED_INIT) + { + responseReason += "Curl Easy Handle initialization failed."; + } + else + { + responseReason += curl_easy_request_w->getErrorString(); + } + curl_easy_request_w->setopt(CURLOPT_FRESH_CONNECT, TRUE); + } + + if (mResponder) + { + mResponder->completedRaw(responseCode, responseReason, sChannels, mOutput); + mResponder = NULL; + } + + resetState(curl_easy_request_w); +} + +//----------------------------------------------------------------------------- +// CurlMultiHandle + +LLAtomicU32 CurlMultiHandle::sTotalMultiHandles; + +CurlMultiHandle::CurlMultiHandle(void) +{ + mMultiHandle = curl_multi_init(); + if (!mMultiHandle) + { + throw AICurlNoMultiHandle("curl_multi_init() returned NULL"); + } + sTotalMultiHandles++; +} + +CurlMultiHandle::~CurlMultiHandle() +{ + curl_multi_cleanup(mMultiHandle); + --sTotalMultiHandles; +} + +} // namespace AICurlPrivate diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h new file mode 100644 index 000000000..ffff2a01e --- /dev/null +++ b/indra/llmessage/aicurl.h @@ -0,0 +1,317 @@ +/** + * @file aicurl.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURL_H +#define AICURL_H + +#include +#include +#include +#include +#include +#include +#include // CURL, CURLM, CURLMcode, CURLoption, curl_*_callback + +// Make sure we don't use this option: it is not thread-safe. +#undef CURLOPT_DNS_USE_GLOBAL_CACHE + +#include "stdtypes.h" // U32 +#include "lliopipe.h" // LLIOPipe::buffer_ptr_t +#include "llatomic.h" // LLAtomicU32 +#include "aithreadsafe.h" + +class LLSD; + +//----------------------------------------------------------------------------- +// Exceptions. +// + +// A general curl exception. +// +class AICurlError : public std::runtime_error { + public: + AICurlError(std::string const& message) : std::runtime_error(message) { } +}; + +class AICurlNoEasyHandle : public AICurlError { + public: + AICurlNoEasyHandle(std::string const& message) : AICurlError(message) { } +}; + +class AICurlNoMultiHandle : public AICurlError { + public: + AICurlNoMultiHandle(std::string const& message) : AICurlError(message) { } +}; + +// End Exceptions. +//----------------------------------------------------------------------------- + +// Things defined in this namespace are called from elsewhere in the viewer code. +namespace AICurlInterface { + +// Output parameter of AICurlPrivate::CurlEasyRequest::getResult. +// Only used by LLXMLRPCTransaction::Impl. +struct TransferInfo { + TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) { } + F64 mSizeDownload; + F64 mTotalTime; + F64 mSpeedDownload; +}; + +//----------------------------------------------------------------------------- +// Global functions. + +// Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)), +// with main purpose to initialize curl. +void initCurl(F32 curl_request_timeout = 120.f, S32 max_number_handles = 256); + +// Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread. +void startCurlThread(void); + +// Called once at end of application (from newview/llappviewer.cpp by main thread), +// with purpose to stop curl threads, free curl resources and deinitialize curl. +void cleanupCurl(void); + +// Called from indra/llmessage/llurlrequest.cpp to print debug output regarding +// an error code returned by EasyRequest::getResult. +// Just returns curl_easy_strerror(errorcode). +std::string strerror(CURLcode errorcode); + +// Called from indra/newview/llfloaterabout.cpp for the About floater, and +// from newview/llappviewer.cpp in behalf of debug output. +// Just returns curl_version(). +std::string getVersionString(void); + +// Called from newview/llappviewer.cpp (and llcrashlogger/llcrashlogger.cpp) to set +// the Certificate Authority file used to verify HTTPS certs. +void setCAFile(std::string const& file); + +// Not called from anywhere. +// Can be used to set the path to the Certificate Authority file. +void setCAPath(std::string const& file); + +//----------------------------------------------------------------------------- +// Global classes. + +// Responder - base class for Request::get* and Request::post API. +// +// The life cycle of classes derived from this class is as follows: +// They are allocated with new on the line where get(), getByteRange() or post() is called, +// and the pointer to the allocated object is then put in a reference counting ResponderPtr. +// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in it's +// member mResponder. Hence, the life time of a Responder is never longer than it's +// associated CurlResponderBuffer, however, if everything works correct, then normally a +// responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting +// mReponder to NULL. +// +// Note that the life time of CurlResponderBuffer is (a bit) shorter than the associated +// CurlEasyRequest (because of the order of base classes of ThreadSafeBufferedCurlEasyRequest) +// and the callbacks, as set by prepRequest, only use those two. +// A callback locks the CurlEasyRequest before actually making the callback, and the +// destruction of CurlResponderBuffer also first locks the CurlEasyRequest, and then revokes +// the callbacks. This assures that a Responder is never used when the objects it uses are +// destructed. Also, if any of those are destructed then the responder is automatically +// destructed too. +// +class Responder { + protected: + Responder(void); + virtual ~Responder(); + + private: + // Associated URL, used for debug output. + std::string mURL; + + public: + // Called to set the url of the current request for this responder, + // used only when printing debug output regarding activity of the responder. + void setURL(std::string const& url); + + public: + // Called from LLHTTPClientURLAdaptor::complete(): + + // Derived classes can override this to get the HTML header that was received, when the message is completed. + // The default does nothing. + virtual void completedHeader(U32 status, std::string const& reason, LLSD const& content); + + // Derived classes can override this to get the raw data of the body of the HTML message that was received. + // The default is to interpret the content as LLSD and call completed(). + virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer); + + // Called from LLHTTPClient request calls, if an error occurs even before we can call one of the above. + // It calls completed() with a fake status U32_MAX, as that is what some derived clients expect (bad design). + // This means that if a derived class overrides completedRaw() it now STILL has to override completed() to catch this error. + void fatalError(std::string const& reason); + + // A derived class should return true if curl should follow redirections. + // The default is not to follow redirections. + virtual bool followRedir(void) { return false; } + + protected: + // ... or, derived classes can override this to get the LLSD content when the message is completed. + // The default is to call result() (or errorWithContent() in case of a HTML status indicating an error). + virtual void completed(U32 status, std::string const& reason, LLSD const& content); + + // ... or, derived classes can override this to received the content of a body upon success. + // The default does nothing. + virtual void result(LLSD const& content); + + // Derived classes can override this to get informed when a bad HTML status code is received. + // The default calls error(). + virtual void errorWithContent(U32 status, std::string const& reason, LLSD const& content); + + // ... or, derived classes can override this to get informed when a bad HTML statis code is received. + // The default prints the error to llinfos. + virtual void error(U32 status, std::string const& reason); + + public: + // Called from LLSDMessage::ResponderAdapter::listener. + // LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these function needs to be public. + + void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); } + void pubResult(LLSD const& content) { result(content); } + + private: + // Used by ResponderPtr. Object is deleted when reference count reaches zero. + LLAtomicU32 mReferenceCount; + + friend void intrusive_ptr_add_ref(Responder* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr is made. + friend void intrusive_ptr_release(Responder* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. + // This function must delete the Responder object when the reference count reaches zero. +}; + +// A Responder is passed around as ResponderPtr, which causes it to automatically +// destruct when there are no pointers left pointing to it. +typedef boost::intrusive_ptr ResponderPtr; + +} // namespace AICurlInterface + +// Forward declaration (see aicurlprivate.h). +namespace AICurlPrivate { + class CurlEasyRequest; +} // namespace AICurlPrivate + +// Define access types (_crat = Const Read Access Type, _rat = Read Access Type, _wat = Write Access Type). +// Typical usage is: +// AICurlEasyRequest h1; // Create easy handle. +// AICurlEasyRequest h2(h1); // Make lightweight copies. +// AICurlEasyRequest_wat h2_w(*h2); // Lock and obtain write access to the easy handle. +// Use *h2_w, which is a reference to the locked CurlEasyRequest instance. +// Note: As it is not allowed to use curl easy handles in any way concurrently, +// read access would at most give access to a CURL const*, which will turn out +// to be completely useless; therefore it is sufficient and efficient to use +// an AIThreadSafeSimple and it's unlikely that AICurlEasyRequest_rat will be used. +typedef AIAccessConst AICurlEasyRequest_rat; +typedef AIAccess AICurlEasyRequest_wat; + +// Events generated by AICurlPrivate::CurlEasyHandle. +struct AICurlEasyHandleEvents { + // Events. + virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + virtual void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; +}; + +#include "aicurlprivate.h" + +// AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a +// builtin type, but wrapping it in AIThreadSafe is obviously not going to help here. +// Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow +// read accesses on it. + +// AICurlEasyRequest: a thread safe, reference counting, auto cleaning curl easy handle. +class AICurlEasyRequest { + public: + // Initial construction is allowed (thread-safe). + // Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed. + // 'new' never returned however and the constructor nor destructor of mCurlEasyRequest is called in this case. + // This might throw AICurlNoEasyHandle. + AICurlEasyRequest(bool buffered) : + mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { } + AICurlEasyRequest(AICurlEasyRequest const& orig) : mCurlEasyRequest(orig.mCurlEasyRequest) { } + + // For the rest, only allow read operations. + AIThreadSafeSimple& operator*(void) const { llassert(mCurlEasyRequest.get()); return *mCurlEasyRequest; } + AIThreadSafeSimple* operator->(void) const { llassert(mCurlEasyRequest.get()); return mCurlEasyRequest.get(); } + AIThreadSafeSimple* get(void) const { return mCurlEasyRequest.get(); } + + // Returns true if this object points to the same CurlEasyRequest object. + bool operator==(AICurlEasyRequest const& cer) const { return mCurlEasyRequest == cer.mCurlEasyRequest; } + + // Returns true if this object points to a different CurlEasyRequest object. + bool operator!=(AICurlEasyRequest const& cer) const { return mCurlEasyRequest != cer.mCurlEasyRequest; } + + // Queue this request for insertion in the multi session. + void addRequest(void); + + // Queue a command to remove this request from the multi session (or cancel a queued command to add it). + void removeRequest(void); + + private: + // The actual pointer to the ThreadSafeCurlEasyRequest instance. + AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest; + + private: + // Assignment would not be thread-safe; we may create this object and read from it. + // Note: Destruction is implicitly assumed thread-safe, as it would be a logic error to + // destruct it while another thread still needs it, concurrent or not. + AICurlEasyRequest& operator=(AICurlEasyRequest const&) { return *this; } + + public: + // The more exotic member functions of this class, to deal with passing this class + // as CURLOPT_PRIVATE pointer to a curl handle and afterwards restore it. + // For "internal use" only; don't use things from AICurlPrivate yourself. + + // It's thread-safe to give read access the underlaying boost::intrusive_ptr. + // It's not OK to then call get() on that and store the AICurlPrivate::ThreadSafeCurlEasyRequest* separately. + AICurlPrivate::CurlEasyRequestPtr const& get_ptr(void) const { return mCurlEasyRequest; } + + // If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr, + // then it's OK to construct a AICurlEasyRequest from it. + // Note that the external AICurlPrivate::CurlEasyRequestPtr needs it's own locking, because + // it's not thread-safe in itself. + AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { } + + // This one is obviously dangerous. It's for use only in MultiHandle::check_run_count. + // See also the long comment in CurlEasyRequest::finalizeRequest with regard to CURLOPT_PRIVATE. + explicit AICurlEasyRequest(AICurlPrivate::ThreadSafeCurlEasyRequest* ptr) : mCurlEasyRequest(ptr) { } +}; + +// Write Access Type for the buffer. +struct AICurlResponderBuffer_wat : public AIAccess { + explicit AICurlResponderBuffer_wat(AICurlPrivate::ThreadSafeBufferedCurlEasyRequest& lockobj) : + AIAccess(lockobj) { } + AICurlResponderBuffer_wat(AIThreadSafeSimple& lockobj) : + AIAccess(static_cast(lockobj)) { } +}; + +#define AICurlPrivate DONTUSE_AICurlPrivate + +#endif diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h new file mode 100644 index 000000000..9a7f50180 --- /dev/null +++ b/indra/llmessage/aicurlprivate.h @@ -0,0 +1,360 @@ +/** + * @file aicurlprivate.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLPRIVATE_H +#define AICURLPRIVATE_H + +#include + +namespace AICurlPrivate { +namespace curlthread { class MultiHandle; } + +CURLcode check_easy_code(CURLcode code); +CURLMcode check_multi_code(CURLMcode code); + +bool curlThreadIsRunning(void); +void wakeUpCurlThread(void); + +class ThreadSafeCurlEasyRequest; +class ThreadSafeBufferedCurlEasyRequest; + +// This class wraps CURL*'s. +// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. +class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEvents { + public: + CurlEasyHandle(void); + ~CurlEasyHandle(); + + private: + // Disallow assignment. + CurlEasyHandle& operator=(CurlEasyHandle const*); + + public: + // Reset all options of a libcurl session handle. + void reset(void) { llassert(!mActiveMultiHandle); curl_easy_reset(mEasyHandle); } + + // Set options for a curl easy handle. + template + CURLcode setopt(CURLoption option, BUILTIN parameter); + + // Clone a libcurl session handle using all the options previously set. + CurlEasyHandle(CurlEasyHandle const& orig) : mEasyHandle(curl_easy_duphandle(orig.mEasyHandle)), mActiveMultiHandle(NULL), mErrorBuffer(NULL) { } + + // URL encode/decode the given string. + char* escape(char* url, int length); + char* unescape(char* url, int inlength , int* outlength); + + // Extract information from a curl handle. + CURLcode getinfo(CURLINFO info, void* data); +#if _WIN64 || __x86_64__ || __ppc64__ + // Overload for integer types that are too small (libcurl demands a long). + CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo(info, &ldata); *data = static_cast(ldata); return res; } + CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo(info, &ldata); *data = static_cast(ldata); return res; } +#endif + + // Perform a file transfer (blocking). + CURLcode perform(void); + // Pause and unpause a connection. + CURLcode pause(int bitmask); + + private: + CURL* mEasyHandle; + CURLM* mActiveMultiHandle; + char* mErrorBuffer; + static LLAtomicU32 sTotalEasyHandles; + + private: + // This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle. + friend class curlthread::MultiHandle; + CURLMcode add_handle_to_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); + CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); + + public: + // Retuns total number of existing CURL* handles (excluding ones created outside this class). + static U32 getTotalEasyHandles(void) { return sTotalEasyHandles; } + + // Returns true if this easy handle was added to a curl multi handle. + bool active(void) const { return mActiveMultiHandle; } + + // Call this prior to every curl_easy function whose return value is passed to check_easy_code. + void setErrorBuffer(void); + + // If there was an error code as result, then this returns a human readable error string. + // Only valid when setErrorBuffer was called and the curl_easy function returned an error. + std::string getErrorString(void) const { return mErrorBuffer; } + + // Used for debugging purposes. + bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; } + + protected: + // Return the underlaying curl easy handle. + CURL* getEasyHandle(void) const { return mEasyHandle; } + + private: + // Return, and possibly create, the curl (easy) error buffer used by the current thread. + static char* getTLErrorBuffer(void); +}; + +template +CURLcode CurlEasyHandle::setopt(CURLoption option, BUILTIN parameter) +{ + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +// CurlEasyRequest adds a slightly more powerful interface that can be used +// to set the options on a curl easy handle. +// +// Calling sendRequest() will then connect to the given URL and perform +// the data exchange. If an error occurs related to this handle, it can +// be read by calling getErrorString(). +// +// Note that the life cycle of a CurlEasyRequest is controlled by AICurlEasyRequest: +// a CurlEasyRequest is only ever created as base class of a ThreadSafeCurlEasyRequest, +// which is only created by creating a AICurlEasyRequest. When the last copy of such +// AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted +// and the CurlEasyRequest destructed. +class CurlEasyRequest : public CurlEasyHandle { + public: + void setoptString(CURLoption option, std::string const& value); + void setPost(char const* postdata, S32 size); + void addHeader(char const* str); + + private: + // Call back stubs. + static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata); + static CURLcode SSLCtxCallback(CURL* curl, void* sslctx, void* userdata); + + curl_write_callback mHeaderCallback; + void* mHeaderCallbackUserData; + curl_write_callback mWriteCallback; + void* mWriteCallbackUserData; + curl_read_callback mReadCallback; + void* mReadCallbackUserData; + curl_ssl_ctx_callback mSSLCtxCallback; + void* mSSLCtxCallbackUserData; + + public: + void setHeaderCallback(curl_write_callback callback, void* userdata); + void setWriteCallback(curl_write_callback callback, void* userdata); + void setReadCallback(curl_read_callback callback, void* userdata); + void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); + + // Call this if the set callbacks are about to be invalidated. + void revokeCallbacks(void); + + // Reset everything to the state it was in when this object was just created. + void resetState(void); + + public: + // Set default options that we want applied to all curl easy handles. + void applyDefaultOptions(void); + + // Prepare the request for adding it to a multi session, or calling perform. + // This actually adds the headers that were collected with addHeader. + void finalizeRequest(std::string const& url); + + // Store result code that is returned by getResult. + void store_result(CURLcode result) { mResult = result; } + + // Called when the curl easy handle is done. + void done(AICurlEasyRequest_wat& curl_easy_request_w) { finished(curl_easy_request_w); } + + // Fill info with the transfer info. + void getTransferInfo(AICurlInterface::TransferInfo* info); + + // If result != CURLE_FAILED_INIT then also info was filled. + void getResult(CURLcode* result, AICurlInterface::TransferInfo* info = NULL); + + private: + curl_slist* mHeaders; + bool mRequestFinalized; + AICurlEasyHandleEvents* mEventsTarget; + CURLcode mResult; + + private: + // This class may only be created by constructing a ThreadSafeCurlEasyRequest. + friend class ThreadSafeCurlEasyRequest; + // Throws AICurlNoEasyHandle. + CurlEasyRequest(void) : + mHeaders(NULL), mRequestFinalized(false), mEventsTarget(NULL), mResult(CURLE_FAILED_INIT) + { applyDefaultOptions(); } + public: + ~CurlEasyRequest(); + + public: + // Post initialization, set the parent to which to pass the events to. + void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; } + + // For debugging purposes + bool is_finalized(void) const { return mRequestFinalized; } + + // Return pointer to the ThreadSafe (wrapped) version of this object. + ThreadSafeCurlEasyRequest* get_lockobj(void); + + protected: + // Pass events to parent. + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); +}; + +// Buffers used by the AICurlInterface::Request API. +// Curl callbacks write into and read from these buffers. +// The interface with the rest of the code is through AICurlInterface::Responder. +// +// The life time of a CurlResponderBuffer is slightly shorter than it's +// associated CurlEasyRequest; this class can only be created as base class +// of ThreadSafeBufferedCurlEasyRequest, and is therefore constructed after +// the construction of the associated CurlEasyRequest and destructed before it. +// Hence, it's safe to use get_lockobj() and through that access the CurlEasyRequest +// object at all times. +// +// A CurlResponderBuffer is thus created when a ThreadSafeBufferedCurlEasyRequest +// is created which only happens by creating a AICurlEasyRequest(true) instance, +// and when the last AICurlEasyRequest is deleted, then the ThreadSafeBufferedCurlEasyRequest +// is deleted and the CurlResponderBuffer destructed. +class CurlResponderBuffer : protected AICurlEasyHandleEvents { + public: + void resetState(AICurlEasyRequest_wat& curl_easy_request_w); + void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, std::vector const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0, bool post = false); + + std::stringstream& getInput() { return mInput; } + std::stringstream& getHeaderOutput() { return mHeaderOutput; } + LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } + + // Called after removed_from_multi_handle was called. + void processOutput(AICurlEasyRequest_wat& curl_easy_request_w); + + protected: + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void finished(AICurlEasyRequest_wat& curl_easy_request_w); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); + + private: + std::stringstream mInput; + std::stringstream mHeaderOutput; + LLIOPipe::buffer_ptr_t mOutput; + AICurlInterface::ResponderPtr mResponder; + + public: + static LLChannelDescriptors const sChannels; // Channel object for mOutput: we ONLY use channel 0, so this can be a constant. + + private: + // This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest. + friend class ThreadSafeBufferedCurlEasyRequest; + CurlResponderBuffer(void); + public: + ~CurlResponderBuffer(); + + private: + static size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data); + static size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data); + static size_t curlHeaderCallback(char* data, size_t size, size_t nmemb, void* user_data); + + public: + // Return pointer to the ThreadSafe (wrapped) version of this object. + ThreadSafeBufferedCurlEasyRequest* get_lockobj(void); +}; + +// This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can +// copy it around cheaply and it gets destructed automatically when the last instance is deleted. +// It guarantees that the CURL* handle is never used concurrently, which is not allowed by libcurl. +// As AIThreadSafeSimpleDC contains a mutex, it cannot be copied. Therefore we need a reference counter for this object. +class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple { + public: + // Throws AICurlNoEasyHandle. + ThreadSafeCurlEasyRequest(void) : mReferenceCount(0) + { new (ptr()) CurlEasyRequest; + Dout(dc::curl, "Creating ThreadSafeCurlEasyRequest with this = " << (void*)this); } + virtual ~ThreadSafeCurlEasyRequest() + { Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); } + + private: + LLAtomicU32 mReferenceCount; + + friend void intrusive_ptr_add_ref(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr is made. + friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. +}; + +// Same as the above but adds a CurlResponderBuffer. The latter has it's own locking in order to +// allow casting the underlaying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of +// what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest. +// The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently +// as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest. +// Note: the order of these base classes is important: AIThreadSafeSimple is now +// destructed before ThreadSafeCurlEasyRequest is. +class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, public AIThreadSafeSimple { + public: + // Throws AICurlNoEasyHandle. + ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple::ptr()) CurlResponderBuffer; } +}; + +// The curl easy request type wrapped in a reference counting pointer. +typedef boost::intrusive_ptr CurlEasyRequestPtr; + +// This class wraps CURLM*'s. +// It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. +class CurlMultiHandle : public boost::noncopyable { + public: + CurlMultiHandle(void); + ~CurlMultiHandle(); + + private: + // Disallow assignment. + CurlMultiHandle& operator=(CurlMultiHandle const*); + + private: + static LLAtomicU32 sTotalMultiHandles; + + protected: + CURLM* mMultiHandle; + + public: + // Set options for a curl multi handle. + template + CURLMcode setopt(CURLMoption option, BUILTIN parameter); + + // Returns total number of existing CURLM* handles (excluding ones created outside this class). + static U32 getTotalMultiHandles(void) { return sTotalMultiHandles; } +}; + +template +CURLMcode CurlMultiHandle::setopt(CURLMoption option, BUILTIN parameter) +{ + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +} // namespace AICurlPrivate + +#endif diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp new file mode 100644 index 000000000..6b2d1d68d --- /dev/null +++ b/indra/llmessage/aicurlthread.cpp @@ -0,0 +1,1054 @@ +/** + * @file aicurlthread.cpp + * @brief Implementation of AICurl, curl thread functions. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aicurlthread.h" +#include "lltimer.h" // ms_sleep +#include +#include +#include +#include +#include + +#undef AICurlPrivate + +namespace AICurlPrivate { + +enum command_st { + cmd_none, + cmd_add, + cmd_boost, + cmd_remove +}; + +class Command { + public: + Command(void) : mCommand(cmd_none) { } + Command(AICurlEasyRequest const& easy_request, command_st command) : mCurlEasyRequest(easy_request.get_ptr()), mCommand(command) { } + + command_st command(void) const { return mCommand; } + CurlEasyRequestPtr const& easy_request(void) const { return mCurlEasyRequest; } + + bool operator==(AICurlEasyRequest const& easy_request) const { return mCurlEasyRequest == easy_request.get_ptr(); } + + void reset(void); + + private: + CurlEasyRequestPtr mCurlEasyRequest; + command_st mCommand; +}; + +void Command::reset(void) +{ + mCurlEasyRequest.reset(); + mCommand = cmd_none; +} + +// The following two globals have separate locks for speed considerations (in order not +// to block the main thread unnecessarily) but have the following correlation: +// +// MAIN-THREAD (AICurlEasyRequest::addRequest) +// * command_queue locked +// - A non-active (mActiveMultiHandle is NULL) ThreadSafeCurlEasyRequest (by means of an AICurlEasyRequest pointing to it) is added to command_queue with as command cmd_add. +// * command_queue unlocked +// +// If at this point addRequest is called again, then it is detected that the last command added to the queue +// for this ThreadSafeCurlEasyRequest is cmd_add. +// +// CURL-THREAD (AICurlThread::wakeup): +// * command_queue locked +// * command_being_processed is write-locked +// - command_being_processed is assigned the value of the command in the queue. +// * command_being_processed is unlocked +// - The command is removed from command_queue +// * command_queue unlocked +// +// If at this point addRequest is called again, then it is detected that command_being_processed adds the same ThreadSafeCurlEasyRequest. +// +// * command_being_processed is read-locked +// - mActiveMultiHandle is set to point to the curl multi handle +// - The easy handle is added to the multi handle +// * command_being_processed is write-locked +// - command_being_processed is reset +// * command_being_processed is unlocked +// +// If at this point addRequest is called again, then it is detected that the ThreadSafeCurlEasyRequest is active. + +// Multi-threaded queue for passing Command objects from the main-thread to the curl-thread. +AIThreadSafeSimpleDC > command_queue; +typedef AIAccess > command_queue_wat; + +AIThreadSafeDC command_being_processed; +typedef AIWriteAccess command_being_processed_wat; +typedef AIReadAccess command_being_processed_rat; + +namespace curlthread { +// All functions in this namespace are only run by the curl thread, unless they are marked with MAIN-THREAD. + +//----------------------------------------------------------------------------- +// PollSet + +// A PollSet can store at least 1024 file descriptors, or FD_SETSIZE if that is larger than 1024 [MAXSIZE]. +// The number of stored file descriptors is mNrFds [0 <= mNrFds <= MAXSIZE]. +// The largest file descriptor is stored is mMaxFd, which is -1 iff mNrFds == 0. +// The file descriptors are stored contiguous in mFileDescriptors[i], with 0 <= i < mNrFds. +// File descriptors with the highest priority should be stored first (low index). +// +// mNext is an index into mFileDescriptors that is copied first, the next call to refresh(). +// It is set to 0 when mNrFds < FD_SETSIZE, even if mNrFds == 0. +// +// After a call to refresh(): +// +// mFdSet has bits set for at most FD_SETSIZE - 1 file descriptors, copied from mFileDescriptors starting +// at index mNext (wrapping around to 0). If mNrFds < FD_SETSIZE then mNext is reset to 0 before copying starts. +// If mNrFds >= FD_SETSIZE then mNext is set to the next filedescriptor that was not copied (otherwise it is left at 0). +// +// mMaxFdSet is the largest filedescriptor in mFdSet or -1 if it is empty. + +// Create an empty PollSet. +PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [std::max(1024, FD_SETSIZE)]), mSize(std::max(1024, FD_SETSIZE)), + mNrFds(0), mMaxFd(-1), mNext(0), mMaxFdSet(-1) +{ + FD_ZERO(&mFdSet); +} + +// Add file descriptor s to the PollSet. +void PollSet::add(curl_socket_t s) +{ + llassert_always(mNrFds < mSize); + mFileDescriptors[mNrFds++] = s; + mMaxFd = std::max(mMaxFd, s); +} + +// Remove file descriptor s from the PollSet. +void PollSet::remove(curl_socket_t s) +{ + // The number of open filedescriptors is relatively small, + // and on top of that we rather do something CPU intensive + // than bandwidth intensive (lookup table). Hence that this + // is a linear search in an array containing just the open + // filedescriptors. Then, since we are reading this memory + // page anyway, we might as well write to it without losing + // much clock cycles. Therefore, shift the whole vector + // back, keeping it compact and keeping the filedescriptors + // in the same order (which is supposedly their priority). + llassert(mNrFds > 0); + int i = --mNrFds; + int prev = mFileDescriptors[i]; + int max = -1; + for (--i; i >= 0 && prev != s; --i) + { + int cur = mFileDescriptors[i]; + mFileDescriptors[i] = prev; + max = std::max(max, prev); + prev = cur; + } + if (s == mMaxFd) + { + while (i >= 0) + { + int cur = mFileDescriptors[i]; + max = std::max(max, cur); + --i; + } + mMaxFd = max; + llassert(mMaxFd < s); + llassert((mMaxFd == -1) == (mNrFds == 0)); + } + if (mNext == mNrFds) + mNext = 0; +} + +bool PollSet::contains(curl_socket_t fd) const +{ + for (int i = 0; i < mNrFds; ++i) + if (mFileDescriptors[i] == fd) + return true; + return false; +} + +inline bool PollSet::is_set(curl_socket_t fd) const +{ + return FD_ISSET(fd, &mFdSet); +} + +inline void PollSet::clr(curl_socket_t fd) +{ + return FD_CLR(fd, &mFdSet); +} + +// This function fills mFdSet with at most FD_SETSIZE - 1 filedescriptors, +// starting at index mNext (updating mNext when not all could be added), +// and updates mMaxFdSet to be the largest fd added to mFdSet, or -1 if it's empty. +refresh_t PollSet::refresh(void) +{ + FD_ZERO(&mFdSet); + mCopiedFileDescriptors.clear(); + + if (mNrFds == 0) + { + mMaxFdSet = -1; + return empty_and_complete; + } + + llassert_always(mNext < mNrFds); + + // Test if mNrFds is larger or equal FD_SETSIZE; equal, because we reserve one + // file descriptor for the wakeup fd: we copy maximal FD_SETSIZE - 1 file descriptors. + // If not then we're going to copy everything so that we can save on CPU cycles + // by not calculating mMaxFdSet here. + if (mNrFds >= FD_SETSIZE) + { + llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; + // Calculate mMaxFdSet. + int max = -1; + int count = 0; + int end = mNrFds; + int i = mNext; + while (++count < FD_SETSIZE) + { + max = std::max(max, mFileDescriptors[i]); + if (++i == end) + { + if (end == mNext) + break; + end = mNext; + i = 0; + llassert(i < end); // If mNext == 0 then the while loop terminates before we get here. + } + } + mMaxFdSet = max; + } + else + { + mNext = 0; // Start at the beginning if we copy everything anyway. + mMaxFdSet = mMaxFd; + } + int count = 0; + int end = mNrFds; + int i = mNext; + for(;;) + { + if (++count == FD_SETSIZE) + { + mNext = i; + return not_complete_not_empty; + } + FD_SET(mFileDescriptors[i], &mFdSet); + mCopiedFileDescriptors.push_back(mFileDescriptors[i]); + if (++i == end) + { + if (mNext == 0) + break; + // If this was true then we got here a second time, which means that we accessed all + // filedescriptors with mNext != 0, but count is still < FD_SETSIZE, which is not + // possible because mNext is only non-zero when mNrFds >= FD_SETSIZE. + llassert(end != mNext); + end = mNext; + i = 0; + } + } + return complete_not_empty; +} + +// FIXME: This needs a rewrite on windows, as FD_ISSET is slow there; it would make +// more sense to iterate directly over the fd's in mFdSet on windows. +void PollSet::reset(void) +{ + llassert((mNrFds == 0) == mCopiedFileDescriptors.empty()); + if (mCopiedFileDescriptors.empty()) + mIter = mCopiedFileDescriptors.end(); + else + { + mIter = mCopiedFileDescriptors.begin(); + if (!FD_ISSET(*mIter, &mFdSet)) + next(); + } +} + +inline int PollSet::get(void) const +{ + return (mIter == mCopiedFileDescriptors.end()) ? -1 : *mIter; +} + +// FIXME: This needs a rewrite on windows, as FD_ISSET is slow there; it would make +// more sense to iterate directly over the fd's in mFdSet on windows. +void PollSet::next(void) +{ + llassert(mIter != mCopiedFileDescriptors.end()); // Only call next() if the last call to get() didn't return -1. + while (++mIter != mCopiedFileDescriptors.end() && !FD_ISSET(*mIter, &mFdSet)); +} + +//----------------------------------------------------------------------------- +// MergeIterator + +class MergeIterator +{ + public: + MergeIterator(PollSet& readPollSet, PollSet& writePollSet); + + bool next(int& fd_out, int& ev_bitmask_out); + + private: + PollSet& mReadPollSet; + PollSet& mWritePollSet; + int readIndx; + int writeIndx; +}; + +MergeIterator::MergeIterator(PollSet& readPollSet, PollSet& writePollSet) : + mReadPollSet(readPollSet), mWritePollSet(writePollSet), readIndx(0), writeIndx(0) +{ + mReadPollSet.reset(); + mWritePollSet.reset(); +} + +bool MergeIterator::next(int& fd_out, int& ev_bitmask_out) +{ + int rfd = mReadPollSet.get(); + int wfd = mWritePollSet.get(); + + if (rfd == -1 && wfd == -1) + return false; + + if (rfd == wfd) + { + fd_out = rfd; + ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; + mReadPollSet.next(); + + } + else if ((unsigned int)rfd < (unsigned int)wfd) // Use and increment smaller one, unless it's -1. + { + fd_out = rfd; + ev_bitmask_out = CURL_CSELECT_IN; + mReadPollSet.next(); + if (wfd != -1 && mWritePollSet.is_set(rfd)) + { + ev_bitmask_out |= CURL_CSELECT_OUT; + mWritePollSet.clr(rfd); + } + } + else + { + fd_out = wfd; + ev_bitmask_out = CURL_CSELECT_OUT; + mWritePollSet.next(); + if (rfd != -1 && mReadPollSet.is_set(wfd)) + { + ev_bitmask_out |= CURL_CSELECT_IN; + mReadPollSet.clr(wfd); + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// CurlSocketInfo + +// A class with info for each socket that is in use by curl. +class CurlSocketInfo +{ + public: + CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action); + ~CurlSocketInfo(); + + void set_action(int action); + + private: + MultiHandle& mMultiHandle; + CURL const* mEasy; + curl_socket_t mSocketFd; + int mAction; +}; + +CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socket_t s, int action) : + mMultiHandle(multi_handle), mEasy(easy), mSocketFd(s), mAction(CURL_POLL_NONE) +{ + mMultiHandle.assign(s, this); + llassert(!mMultiHandle.mReadPollSet.contains(s)); + llassert(!mMultiHandle.mWritePollSet.contains(s)); + set_action(action); +} + +CurlSocketInfo::~CurlSocketInfo() +{ + set_action(CURL_POLL_NONE); +} + +void CurlSocketInfo::set_action(int action) +{ + int toggle_action = mAction ^ action; + mAction = action; + if ((toggle_action & CURL_POLL_IN)) + { + if ((action & CURL_POLL_IN)) + mMultiHandle.mReadPollSet.add(mSocketFd); + else + mMultiHandle.mReadPollSet.remove(mSocketFd); + } + if ((toggle_action & CURL_POLL_OUT)) + { + if ((action & CURL_POLL_OUT)) + mMultiHandle.mWritePollSet.add(mSocketFd); + else + mMultiHandle.mWritePollSet.remove(mSocketFd); + } +} + +//----------------------------------------------------------------------------- +// AICurlThread + +class AICurlThread : public LLThread +{ + public: + static AICurlThread* sInstance; + + public: + // MAIN-THREAD + AICurlThread(void); + virtual ~AICurlThread(); + + // MAIN-THREAD + void wakeup_thread(void); + + protected: + virtual void run(void); + void wakeup(AICurlMultiHandle_wat const& multi_handle_w); + + private: + // MAIN-THREAD + void create_wakeup_fds(void); + void cleanup_wakeup_fds(void); + + int mWakeUpFd_in; + int mWakeUpFd; + + int mZeroTimeOut; +}; + +// Only the main thread is accessing this. +AICurlThread* AICurlThread::sInstance = NULL; + +// MAIN-THREAD +AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(-1), mWakeUpFd(-1), mZeroTimeOut(0) +{ + create_wakeup_fds(); + sInstance = this; +} + +// MAIN-THREAD +AICurlThread::~AICurlThread() +{ + sInstance = NULL; + cleanup_wakeup_fds(); +} + +// MAIN-THREAD +void AICurlThread::create_wakeup_fds(void) +{ +#ifdef WINDOWS +// Probably need to use sockets here, cause windows select doesn't work for a pipe. +#error Missing implementation +#else + int pipefd[2]; + if (pipe(pipefd)) + { + llerrs << "Failed to create wakeup pipe: " << strerror(errno) << llendl; + } + long flags = O_NONBLOCK; + for (int i = 0; i < 2; ++i) + { + if (fcntl(pipefd[i], F_SETFL, flags)) + { + llerrs << "Failed to set pipe to non-blocking: " << strerror(errno) << llendl; + } + } + mWakeUpFd = pipefd[0]; // Read-end of the pipe. + mWakeUpFd_in = pipefd[1]; // Write-end of the pipe. +#endif +} + +// MAIN-THREAD +void AICurlThread::cleanup_wakeup_fds(void) +{ + if (mWakeUpFd_in != -1) + close(mWakeUpFd_in); + if (mWakeUpFd != -1) + close(mWakeUpFd); +} + +// MAIN-THREAD +void AICurlThread::wakeup_thread(void) +{ + DoutEntering(dc::curl, "AICurlThread::wakeup_thread"); + +#ifdef WINDOWS +#error Missing implementation +#else + // If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. + // If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. + // Write requests to a pipe or FIFO shall be handled in the same way as a regular file with the following exceptions: + // If the O_NONBLOCK flag is set, write() requests shall be handled differently, in the following ways: + // A write request for {PIPE_BUF} or fewer bytes shall have the following effect: + // if there is sufficient space available in the pipe, write() shall transfer all the data and return the number + // of bytes requested. Otherwise, write() shall transfer no data and return -1 with errno set to [EAGAIN]. + ssize_t len; + do + { + len = write(mWakeUpFd_in, "!", 1); + if (len == -1 && errno == EAGAIN) + return; // Unread characters are still in the pipe, so no need to add more. + } + while(len == -1 && errno == EINTR); + if (len == -1) + { + llerrs << "write(3) to mWakeUpFd_in: " << strerror(errno) << llendl; + } + llassert_always(len == 1); +#endif +} + +void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) +{ + DoutEntering(dc::curl, "AICurlThread::wakeup"); + +#ifdef WINDOWS +#error Missing implementation +#else + // If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR]. + // If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read. + // When attempting to read from an empty pipe or FIFO: + // If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file. + // If some process has the pipe open for writing and O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. + char buf[256]; + ssize_t len; + do + { + len = read(mWakeUpFd, buf, sizeof(buf)); + if (len == -1 && errno == EAGAIN) + return; + } + while(len == -1 && errno == EINTR); + if (len == -1) + { + llerrs << "read(3) from mWakeUpFd: " << strerror(errno) << llendl; + } + if (LL_UNLIKELY(len == 0)) + { + llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; + close(mWakeUpFd); + mWakeUpFd = -1; + shutdown(); + return; + } +#endif + // If we get here then the main thread called wakeup_thread() recently. + for(;;) + { + // Access command_queue, and move command to command_being_processed. + { + command_queue_wat command_queue_w(command_queue); + if (command_queue_w->empty()) + break; + // Move the next command from the queue into command_being_processed. + *command_being_processed_wat(command_being_processed) = command_queue_w->front(); + command_queue_w->pop_front(); + } + // Access command_being_processed only. + { + command_being_processed_rat command_being_processed_r(command_being_processed); + switch(command_being_processed_r->command()) + { + case cmd_none: + case cmd_boost: // FIXME: future stuff + break; + case cmd_add: + multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); + break; + case cmd_remove: + multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); + break; + } + // Done processing. + command_being_processed_wat command_being_processed_w(command_being_processed_r); + command_being_processed_w->reset(); // This destroys the CurlEasyRequest in case of a cmd_remove. + } + } +} + +// The main loop of the curl thread. +void AICurlThread::run(void) +{ + DoutEntering(dc::curl, "AICurlThread::run()"); + + AICurlMultiHandle_wat multi_handle_w(AICurlMultiHandle::getInstance()); + for(;;) + { + refresh_t rres = multi_handle_w->mReadPollSet.refresh(); + refresh_t wres = multi_handle_w->mWritePollSet.refresh(); + fd_set* read_fd_set = multi_handle_w->mReadPollSet.access(); + if (LL_LIKELY(mWakeUpFd != -1)) + FD_SET(mWakeUpFd, read_fd_set); + else if ((rres & empty)) + read_fd_set = NULL; + fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); + int const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); + int const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); + int nfds = std::max(max_rfd, max_wfd) + 1; + llassert(0 <= nfds && nfds <= FD_SETSIZE); + llassert((max_rfd == -1) == (read_fd_set == NULL) && + (max_wfd == -1) == (write_fd_set == NULL)); // Needed on windows. + llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && + (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); + int ready = 0; + if (LL_UNLIKELY(nfds == 0)) // Only happens when the thread is shutting down. + ms_sleep(1000); + else + { + struct timeval timeout; + long timeout_ms = multi_handle_w->getTimeOut(); + // If no timeout is set, sleep 1 second. + if (timeout_ms < 0) + timeout_ms = 1000; + if (timeout_ms == 0) + { + if (mZeroTimeOut >= 10000) + { + if (mZeroTimeOut == 10000) + llwarns << "Detected more than 10000 zero-timeout calls of select() by curl thread (more than 101 seconds)!" << llendl; + } + else if (mZeroTimeOut >= 1000) + timeout_ms = 10; + else if (mZeroTimeOut >= 100) + timeout_ms = 1; + } + else + { + if (mZeroTimeOut >= 10000) + llinfos << "Timeout of select() call by curl thread reset (to " << timeout_ms << " ms)." << llendl; + mZeroTimeOut = 0; + } + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; +#ifdef CWDEBUG + static int last_nfds = -1; + static long last_timeout_ms = -1; + static int same_count = 0; + bool same = (nfds == last_nfds && timeout_ms == last_timeout_ms); + if (!same) + { + if (same_count > 1) + Dout(dc::curl, "Last select() call repeated " << same_count << " times."); + Dout(dc::curl|flush_cf|continued_cf, "select(" << nfds << ", ..., timeout = " << timeout_ms << " ms) = "); + same_count = 1; + } + else + { + ++same_count; + } +#endif + ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout); +#ifdef CWDEBUG + static int last_ready = -2; + static int last_errno = 0; + if (!same) + Dout(dc::finish|cond_error_cf(ready == -1), ready); + else if (ready != last_ready || (ready == -1 && errno != last_errno)) + { + if (same_count > 1) + Dout(dc::curl, "Last select() call repeated " << same_count << " times."); + Dout(dc::curl|cond_error_cf(ready == -1), "select(" << last_nfds << ", ..., timeout = " << last_timeout_ms << " ms) = " << ready); + same_count = 1; + } + last_nfds = nfds; + last_timeout_ms = timeout_ms; + last_ready = ready; + if (ready == -1) + last_errno = errno; +#endif + } + // Select returns the total number of bits set in each of the fd_set's (upon return), + // or -1 when an error occurred. A value of 0 means that a timeout occurred. + if (ready == -1) + { + llwarns << "select() failed: " << errno << ", " << strerror(errno) << llendl; + continue; + } + else if (ready == 0) + { + multi_handle_w->socket_action(CURL_SOCKET_TIMEOUT, 0); + } + else + { + if (multi_handle_w->mReadPollSet.is_set(mWakeUpFd)) + { + wakeup(multi_handle_w); + --ready; + } + MergeIterator iter(multi_handle_w->mReadPollSet, multi_handle_w->mWritePollSet); + int fd, ev_bitmask; + while (ready > 0 && iter.next(fd, ev_bitmask)) + { + ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; + multi_handle_w->socket_action(fd, ev_bitmask); + llassert(ready >= 0); + } + llassert(ready == 0); + } + multi_handle_w->check_run_count(); + } +} + +//----------------------------------------------------------------------------- +// MultiHandle + +MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandles(0), mRunningHandles(0), mTimeOut(-1) +{ + curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETFUNCTION, &MultiHandle::socket_callback); + curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETDATA, this); + curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERFUNCTION, &MultiHandle::timer_callback); + curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERDATA, this); +} + +MultiHandle::~MultiHandle() +{ + // This thread was terminated. + // Curl demands that all handles are removed from the multi session handle before calling curl_multi_cleanup. + for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end(); iter = mAddedEasyRequests.begin()) + { + remove_easy_request(*iter); + } +} + +#ifdef CWDEBUG +#undef AI_CASE_RETURN +#define AI_CASE_RETURN(x) do { case x: return #x; } while(0) +char const* action_str(int action) +{ + switch(action) + { + AI_CASE_RETURN(CURL_POLL_NONE); + AI_CASE_RETURN(CURL_POLL_IN); + AI_CASE_RETURN(CURL_POLL_OUT); + AI_CASE_RETURN(CURL_POLL_INOUT); + AI_CASE_RETURN(CURL_POLL_REMOVE); + } + return ""; +} +#endif + +//static +int MultiHandle::socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp) +{ + DoutEntering(dc::curl, "MultiHandle::socket_callback(" << (void*)easy << ", " << s << ", " << action_str(action) << ", " << (void*)userp << ", " << (void*)socketp << ")"); + MultiHandle& self = *static_cast(userp); + CurlSocketInfo* sock_info = static_cast(socketp); + if (action == CURL_POLL_REMOVE) + { + delete sock_info; + } + else + { + if (!sock_info) + { + sock_info = new CurlSocketInfo(self, easy, s, action); + } + else + { + sock_info->set_action(action); + } + } + return 0; +} + +//static +int MultiHandle::timer_callback(CURLM* multi, long timeout_ms, void* userp) +{ + MultiHandle& self = *static_cast(userp); + llassert(multi == self.mMultiHandle); + self.mTimeOut = timeout_ms; + Dout(dc::curl, "MultiHandle::timer_callback(): timeout set to " << timeout_ms << " ms."); + return 0; +} + +CURLMcode MultiHandle::socket_action(curl_socket_t sockfd, int ev_bitmask) +{ + CURLMcode res; + do + { + res = check_multi_code(curl_multi_socket_action(mMultiHandle, sockfd, ev_bitmask, &mRunningHandles)); + } + while(res == CURLM_CALL_MULTI_PERFORM); + return res; +} + +CURLMcode MultiHandle::assign(curl_socket_t sockfd, void* sockptr) +{ + return check_multi_code(curl_multi_assign(mMultiHandle, sockfd, sockptr)); +} + +CURLMsg const* MultiHandle::info_read(int* msgs_in_queue) const +{ + return curl_multi_info_read(mMultiHandle, msgs_in_queue); +} + +CURLMcode MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) +{ + std::pair res = mAddedEasyRequests.insert(easy_request); + llassert(res.second); // May not have been added before. + CURLMcode ret; + { + AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + ret = curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle); + } + mHandleAddedOrRemoved = true; + Dout(dc::curl, "MultiHandle::add_easy_request: Added AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + return ret; +} + +CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request) +{ + addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request); + if (iter == mAddedEasyRequests.end()) + return (CURLMcode)-2; // Was already removed before. + CURLMcode res; + { + AICurlEasyRequest_wat curl_easy_request_w(**iter); + res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); + } + mAddedEasyRequests.erase(iter); + mHandleAddedOrRemoved = true; + Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)easy_request.get() << "; now processing " << mAddedEasyRequests.size() << " easy handles."); + return res; +} + +void MultiHandle::check_run_count(void) +{ + if (mHandleAddedOrRemoved || mRunningHandles < mPrevRunningHandles) + { + CURLMsg const* msg; + int msgs_left; + while ((msg = info_read(&msgs_left))) + { + if (msg->msg == CURLMSG_DONE) + { + CURL* easy = msg->easy_handle; + ThreadSafeCurlEasyRequest* ptr; + curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr); + AICurlEasyRequest easy_request(ptr); + llassert(*AICurlEasyRequest_wat(*easy_request) == easy); + // Store the result and transfer info in the easy handle. + { + AICurlEasyRequest_wat curl_easy_request_w(*easy_request); + curl_easy_request_w->store_result(msg->data.result); +#ifdef CWDEBUG + char* eff_url; + curl_easy_request_w->getinfo(CURLINFO_EFFECTIVE_URL, &eff_url); + Dout(dc::curl, "Finished: " << eff_url << " (" << msg->data.result << ")"); +#endif + // Signal that this easy handle finished. + curl_easy_request_w->done(curl_easy_request_w); + } + // This invalidates msg, but not easy_request. + CURLMcode res = remove_easy_request(easy_request); + // This should hold, I think, because the handles are obviously ok and + // the only error we could get is when remove_easy_request() was already + // called before (by this thread); but if that was the case then the easy + // handle should not have been be returned by info_read()... + llassert(res == CURLM_OK); + // Nevertheless, if it was already removed then just ignore it. + if (res == CURLM_OK) + { + } + else if (res == -2) + { + llwarns << "Curl easy handle returned by curl_multi_info_read() that is not (anymore) in MultiHandle::mAddedEasyRequests!?!" << llendl; + } + // Destruction of easy_request at this point, causes the CurlEasyRequest to be deleted. + } + } + mHandleAddedOrRemoved = false; + } + mPrevRunningHandles = mRunningHandles; +} + +} // namespace curlthread +} // namespace AICurlPrivate + +//============================================================================= +// MAIN-THREAD (needing to access the above declarations). + +//static +AICurlMultiHandle& AICurlMultiHandle::getInstance(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + if (!tldata.mCurlMultiHandle) + { + llinfos << "Creating AICurlMultiHandle for thread \"" << tldata.mName << "\"." << llendl; + tldata.mCurlMultiHandle = new AICurlMultiHandle; + } + return *static_cast(tldata.mCurlMultiHandle); +} + +namespace AICurlPrivate { + +bool curlThreadIsRunning(void) +{ + using curlthread::AICurlThread; + return AICurlThread::sInstance && !AICurlThread::sInstance->isStopped(); +} + +void wakeUpCurlThread(void) +{ + using curlthread::AICurlThread; + if (AICurlThread::sInstance) + AICurlThread::sInstance->wakeup_thread(); +} + +} // namespace AICurlPrivate + +//----------------------------------------------------------------------------- +// AICurlEasyRequest + +void AICurlEasyRequest::addRequest(void) +{ + using namespace AICurlPrivate; + + { + // Write-lock the command queue. + command_queue_wat command_queue_w(command_queue); +#ifdef SHOW_ASSERT + // This debug code checks if we aren't calling addRequest() twice for the same object. + // That means that the main thread already called (and finished, this is also the + // main thread) this function, which also follows from that we just locked command_queue. + // That leaves three options: It's still in the queue, or it was removed and is currently + // processed by the curl thread with again two options: either it was already added + // to the multi session handle or not yet. + + // Find the last command added. + command_st cmd = cmd_none; + for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + { + if (*iter == *this) + { + cmd = iter->command(); + break; + } + } + llassert(cmd == cmd_none || cmd == cmd_remove); // Not in queue, or last command was to remove it. + if (cmd == cmd_none) + { + // Read-lock command_being_processed. + command_being_processed_rat command_being_processed_r(command_being_processed); + if (*command_being_processed_r == *this) + { + // May not be inbetween being removed from the command queue but not added to the multi session handle yet. + llassert(command_being_processed_r->command() == cmd_remove); + } + else + { + // May not already be added to the multi session handle. + llassert(!AICurlEasyRequest_wat(*get())->active()); + } + } +#endif + // Add a command to add the new request to the multi session to the command queue. + command_queue_w->push_back(Command(*this, cmd_add)); + } + // Something was added to the queue, wake up the thread to get it. + wakeUpCurlThread(); +} + +void AICurlEasyRequest::removeRequest(void) +{ + using namespace AICurlPrivate; + + { + // Write-lock the command queue. + command_queue_wat command_queue_w(command_queue); +#ifdef SHOW_ASSERT + // This debug code checks if we aren't calling removeRequest() twice for the same object. + // That means that the thread calling this function already finished it, following from that + // we just locked command_queue. + // That leaves three options: It's still in the queue, or it was removed and is currently + // processed by the curl thread with again two options: either it was already removed + // from the multi session handle or not yet. + + // Find the last command added. + command_st cmd = cmd_none; + for (std::deque::iterator iter = command_queue_w->begin(); iter != command_queue_w->end(); ++iter) + { + if (*iter == *this) + { + cmd = iter->command(); + break; + } + } + llassert(cmd == cmd_none || cmd != cmd_remove); // Not in queue, or last command was not a remove command. + if (cmd == cmd_none) + { + // Read-lock command_being_processed. + command_being_processed_rat command_being_processed_r(command_being_processed); + if (*command_being_processed_r == *this) + { + // May not be inbetween being removed from the command queue but not removed from the multi session handle yet. + llassert(command_being_processed_r->command() != cmd_remove); + } + else + { + // May not already have been removed from multi session handle. + llassert(AICurlEasyRequest_wat(*get())->active()); + } + } +#endif + // Add a command to remove this request from the multi session to the command queue. + command_queue_w->push_back(Command(*this, cmd_remove)); + } + // Something was added to the queue, wake up the thread to get it. + wakeUpCurlThread(); +} + +//----------------------------------------------------------------------------- + +namespace AICurlInterface { + +void startCurlThread(void) +{ + using namespace AICurlPrivate::curlthread; + + llassert(is_main_thread()); + AICurlThread::sInstance = new AICurlThread; + AICurlThread::sInstance->start(); +} + +} // namespace AICurlInterface + diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h new file mode 100644 index 000000000..448e53ac0 --- /dev/null +++ b/indra/llmessage/aicurlthread.h @@ -0,0 +1,185 @@ +/** + * @file aicurlthread.h + * @brief Thread safe wrapper for libcurl. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/04/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLTHREAD_H +#define AICURLTHREAD_H + +#include "aicurl.h" +#include + +#undef AICurlPrivate + +namespace AICurlPrivate { +namespace curlthread { + +// For ordering a std::set with AICurlEasyRequest objects. +struct AICurlEasyRequestCompare { + bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); } +}; + +//----------------------------------------------------------------------------- +// PollSet + +int const empty = 0x1; +int const complete = 0x2; + +enum refresh_t { + not_complete_not_empty = 0, + complete_not_empty = complete, + empty_and_complete = complete|empty +}; + +class PollSet +{ + public: + PollSet(void); + + // Add/remove a filedescriptor to/from mFileDescriptors. + void add(curl_socket_t s); + void remove(curl_socket_t s); + + // Copy mFileDescriptors to an internal fd_set that is returned by access(). + // Returns if all fds could be copied (complete) and/or if the resulting fd_set is empty. + refresh_t refresh(void); + + // Return a pointer to the underlaying fd_set. + fd_set* access(void) { return &mFdSet; } + + // Return the largest fd set in mFdSet by refresh. + int get_max_fd(void) const { return mMaxFdSet; } + + // Return true if a filedescriptor is set in mFileDescriptors (used for debugging). + bool contains(curl_socket_t s) const; + + // Return true if a filedescriptor is set in mFdSet. + bool is_set(curl_socket_t s) const; + + // Clear filedescriptor in mFdSet. + void clr(curl_socket_t fd); + + // Iterate over all file descriptors that were set by refresh and are still set in mFdSet. + void reset(void); // Reset the iterator. + int get(void) const; // Return next filedescriptor, or -1 when there are no more. + // Only valid if reset() was called after the last call to refresh(). + void next(void); // Advance to next filedescriptor. + + private: + curl_socket_t* mFileDescriptors; + size_t mSize; // Size of mFileDescriptors array. + int mNrFds; // The number of filedescriptors in the array. + int mMaxFd; // The largest filedescriptor in the array, or -1 when it is empty. + int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). + + fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). + int mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or -1 when it was empty. + + std::vector mCopiedFileDescriptors; // File descriptors copied by refresh to mFdSet. + std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. +}; + +//----------------------------------------------------------------------------- +// MultiHandle + +// This class adds member functions that will only be called from the AICurlThread thread. +// This class guarantees that all added easy handles will be removed from the multi handle +// before the multi handle is cleaned up, as is required by libcurl. +class MultiHandle : public CurlMultiHandle +{ + public: + MultiHandle(void); + ~MultiHandle(); + + // Add/remove an easy handle to/from a multi session. + CURLMcode add_easy_request(AICurlEasyRequest const& easy_request); + CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request); + + // Reads/writes available data from a particular socket (non-blocking). + CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask); + + // Set data to association with an internal socket. + CURLMcode assign(curl_socket_t sockfd, void* sockptr); + + // Read multi stack informationals. + CURLMsg const* info_read(int* msgs_in_queue) const; + + private: + typedef std::set addedEasyRequests_type; + addedEasyRequests_type mAddedEasyRequests; + + bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count(). + int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with. + int mRunningHandles; // The last value returned by curl_multi_socket_action. + long mTimeOut; // The last time out in ms as set by the call back CURLMOPT_TIMERFUNCTION. + + private: + static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); + static int timer_callback(CURLM* multi, long timeout_ms, void* userp); + + public: + // Returns the number of active easy handles as reported by the last call to curl_multi_socket_action. + int getRunningHandles(void) const { return mRunningHandles; } + + // Returns how long to wait for socket action before calling socket_action(CURL_SOCKET_TIMEOUT, 0), in ms. + int getTimeOut(void) const { return mTimeOut; } + + // This is called before sleeping, after calling (one or more times) socket_action. + void check_run_count(void); + + public: + //----------------------------------------------------------------------------- + // Curl socket administration: + + PollSet mReadPollSet; + PollSet mWritePollSet; +}; + +} // namespace curlthread +} // namespace AICurlPrivate + +// Thread safe, noncopyable curl multi handle. +// This class wraps MultiHandle for thread-safety. +// AIThreadSafeSingleThreadDC cannot be copied, but that is OK as we don't need that (or want that); +// this class provides a thread-local singleton (exactly one instance per thread), and because it +// can't be copied, that guarantees that the CURLM* handle is never used concurrently, which is +// not allowed by libcurl. +class AICurlMultiHandle : public AIThreadSafeSingleThreadDC, public LLThreadLocalDataMember { + public: + static AICurlMultiHandle& getInstance(void); + private: + // Use getInstance(). + AICurlMultiHandle(void) { } +}; + +typedef AISTAccessConst AICurlMultiHandle_rat; +typedef AISTAccess AICurlMultiHandle_wat; + +#define AICurlPrivate DONTUSE_AICurlPrivate + +#endif diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index b2f5ce471..585e4644b 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -26,32 +26,6 @@ * $/LicenseInfo$ */ -#if LL_WINDOWS -#define SAFE_SSL 1 -#elif LL_DARWIN -#define SAFE_SSL 1 -#else -#define SAFE_SSL 1 -#endif - -#include "linden_common.h" - -#include "llcurl.h" - -#include -#include -#include -#if SAFE_SSL -#include -#endif - -#include "llbufferstream.h" -#include "llproxy.h" -#include "llsdserialize.h" -#include "llstl.h" -#include "llthread.h" -#include "lltimer.h" - ////////////////////////////////////////////////////////////////////////////// /* The trick to getting curl to do keep-alives is to reuse the @@ -75,179 +49,37 @@ static const S32 MULTI_PERFORM_CALL_REPEAT = 5; static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds per operation static const S32 MAX_ACTIVE_REQUEST_COUNT = 100; -// DEBUG // -S32 gCurlEasyCount = 0; -S32 gCurlMultiCount = 0; - -////////////////////////////////////////////////////////////////////////////// - //static -std::vector LLCurl::sSSLMutex; -std::string LLCurl::sCAPath; -std::string LLCurl::sCAFile; -LLCurlThread* LLCurl::sCurlThread = NULL ; -LLMutex* LLCurl::sHandleMutexp = NULL ; -S32 LLCurl::sTotalHandles = 0 ; -bool LLCurl::sNotQuitting = true; -F32 LLCurl::sCurlRequestTimeOut = 120.f; //seonds +F32 LLCurl::sCurlRequestTimeOut = 120.f; //seconds S32 LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined). -void check_curl_code(CURLcode code) -{ - if (code != CURLE_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - llinfos << "curl error detected: " << curl_easy_strerror(code) << llendl; - } -} - -void check_curl_multi_code(CURLMcode code) -{ - if (code != CURLM_OK) - { - // linux appears to throw a curl error once per session for a bad initialization - // at a pretty random time (when enabling cookies). - llinfos << "curl multi error detected: " << curl_multi_strerror(code) << llendl; - } -} - -//static -void LLCurl::setCAPath(const std::string& path) -{ - sCAPath = path; -} - -//static -void LLCurl::setCAFile(const std::string& file) -{ - sCAFile = file; -} - -//static -std::string LLCurl::getVersionString() -{ - return std::string(curl_version()); -} - ////////////////////////////////////////////////////////////////////////////// -LLCurl::Responder::Responder() - : mReferenceCount(0) -{ -} - -LLCurl::Responder::~Responder() -{ -} - -// virtual -void LLCurl::Responder::errorWithContent( - U32 status, - const std::string& reason, - const LLSD&) -{ - error(status, reason); -} - -// virtual -void LLCurl::Responder::error(U32 status, const std::string& reason) -{ - llinfos << mURL << " [" << status << "]: " << reason << llendl; -} - -// virtual -void LLCurl::Responder::result(const LLSD& content) -{ -} - -void LLCurl::Responder::setURL(const std::string& url) -{ - mURL = url; -} - -// virtual -void LLCurl::Responder::completedRaw( - U32 status, - const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) -{ - LLSD content; - LLBufferStream istr(channels, buffer.get()); - if (!LLSDSerialize::fromXML(content, istr)) - { - llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl; - } - - completed(status, reason, content); -} - -// virtual -void LLCurl::Responder::completed(U32 status, const std::string& reason, const LLSD& content) -{ - if (isGoodStatus(status)) - { - result(content); - } - else - { - errorWithContent(status, reason, content); - } -} - -//virtual -void LLCurl::Responder::completedHeader(U32 status, const std::string& reason, const LLSD& content) -{ - -} - -//namespace boost -//{ - void intrusive_ptr_add_ref(LLCurl::Responder* p) - { - ++p->mReferenceCount; - } - - void intrusive_ptr_release(LLCurl::Responder* p) - { - if (p && 0 == --p->mReferenceCount) - { - delete p; - } - } -//}; - - -////////////////////////////////////////////////////////////////////////////// - -std::set LLCurl::Easy::sFreeHandles; -std::set LLCurl::Easy::sActiveHandles; -LLMutex* LLCurl::Easy::sHandleMutexp = NULL ; +AIThreadSafeSimpleDC LLCurl::Easy::sHandles; //static CURL* LLCurl::Easy::allocEasyHandle() { - llassert(LLCurl::getCurlThread()) ; + llassert(*AIAccess(LLCurl::getCurlThread())) ; CURL* ret = NULL; - LLMutexLock lock(sHandleMutexp) ; + //*** Multi-threaded. + AIAccess handles_w(sHandles); - if (sFreeHandles.empty()) + if (handles_w->free.empty()) { ret = LLCurl::newEasyHandle(); } else { - ret = *(sFreeHandles.begin()); - sFreeHandles.erase(ret); - curl_easy_reset(ret); + ret = *(handles_w->free.begin()); + handles_w->free.erase(ret); } if (ret) { - sActiveHandles.insert(ret); + handles_w->active.insert(ret); } return ret; @@ -256,6 +88,9 @@ CURL* LLCurl::Easy::allocEasyHandle() //static void LLCurl::Easy::releaseEasyHandle(CURL* handle) { + DoutEntering(dc::curl, "LLCurl::Easy::releaseEasyHandle(" << (void*)handle << ")"); + BACKTRACE; + static const S32 MAX_NUM_FREE_HANDLES = 32 ; if (!handle) @@ -264,14 +99,17 @@ void LLCurl::Easy::releaseEasyHandle(CURL* handle) //llerrs << "handle cannot be NULL!" << llendl; } - LLMutexLock lock(sHandleMutexp) ; - if (sActiveHandles.find(handle) != sActiveHandles.end()) - { - sActiveHandles.erase(handle); + //*** Multi-Threaded (logout only?) + AIAccess handles_w(sHandles); - if(sFreeHandles.size() < MAX_NUM_FREE_HANDLES) + if (handles_w->active.find(handle) != handles_w->active.end()) + { + handles_w->active.erase(handle); + + if (handles_w->free.size() < MAX_NUM_FREE_HANDLES) { - sFreeHandles.insert(handle); + curl_easy_reset(handle); + handles_w->free.insert(handle); } else { @@ -284,780 +122,16 @@ void LLCurl::Easy::releaseEasyHandle(CURL* handle) } } -LLCurl::Easy::Easy() - : mHeaders(NULL), - mCurlEasyHandle(NULL) -{ - mErrorBuffer[0] = 0; -} - -LLCurl::Easy* LLCurl::Easy::getEasy() -{ - Easy* easy = new Easy(); - easy->mCurlEasyHandle = allocEasyHandle(); - - if (!easy->mCurlEasyHandle) - { - // this can happen if we have too many open files (fails in c-ares/ares_init.c) - llwarns << "allocEasyHandle() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; - delete easy; - return NULL; - } - - // set no DNS caching as default for all easy handles. This prevents them adopting a - // 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); - - ++gCurlEasyCount; - return easy; -} - LLCurl::Easy::~Easy() { - releaseEasyHandle(mCurlEasyHandle); - --gCurlEasyCount; - curl_slist_free_all(mHeaders); - for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); - - if (mResponder && LLCurl::sNotQuitting) //aborted + AISTAccess responder_w(mResponder); + if (*responder_w && LLCurl::getNotQuitting()) //aborted { std::string reason("Request timeout, aborted.") ; - mResponder->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort + (*responder_w)->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort reason, mChannels, mOutput); } - mResponder = NULL; -} - -void LLCurl::Easy::resetState() -{ - curl_easy_reset(mCurlEasyHandle); - - if (mHeaders) - { - curl_slist_free_all(mHeaders); - mHeaders = NULL; - } - - mRequest.str(""); - mRequest.clear(); - - mOutput.reset(); - - mInput.str(""); - mInput.clear(); - - mErrorBuffer[0] = 0; - - mHeaderOutput.str(""); - mHeaderOutput.clear(); -} - -void LLCurl::Easy::setErrorBuffer() -{ - setopt(CURLOPT_ERRORBUFFER, &mErrorBuffer); -} - -const char* LLCurl::Easy::getErrorBuffer() -{ - return mErrorBuffer; -} - -void LLCurl::Easy::setCA() -{ - if (!sCAPath.empty()) - { - setoptString(CURLOPT_CAPATH, sCAPath); - } - if (!sCAFile.empty()) - { - setoptString(CURLOPT_CAINFO, sCAFile); - } -} - -void LLCurl::Easy::setHeaders() -{ - setopt(CURLOPT_HTTPHEADER, mHeaders); -} - -void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) -{ - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SIZE_DOWNLOAD, &info->mSizeDownload)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_TOTAL_TIME, &info->mTotalTime)); - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_SPEED_DOWNLOAD, &info->mSpeedDownload)); -} - -U32 LLCurl::Easy::report(CURLcode code) -{ - U32 responseCode = 0; - std::string responseReason; - - if (code == CURLE_OK) - { - check_curl_code(curl_easy_getinfo(mCurlEasyHandle, CURLINFO_RESPONSE_CODE, &responseCode)); - //*TODO: get reason from first line of mHeaderOutput - } - else - { - responseCode = 499; - responseReason = strerror(code) + " : " + mErrorBuffer; - setopt(CURLOPT_FRESH_CONNECT, TRUE); - } - - if (mResponder) - { - mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput); - mResponder = NULL; - } - - resetState(); - return responseCode; -} - -// Note: these all assume the caller tracks the value (i.e. keeps it persistant) -void LLCurl::Easy::setopt(CURLoption option, S32 value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, void* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -void LLCurl::Easy::setopt(CURLoption option, char* value) -{ - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, value); - check_curl_code(result); -} - -// Note: this copies the string so that the caller does not have to keep it around -void LLCurl::Easy::setoptString(CURLoption option, const std::string& value) -{ - char* tstring = new char[value.length()+1]; - strcpy(tstring, value.c_str()); - mStrings.push_back(tstring); - CURLcode result = curl_easy_setopt(mCurlEasyHandle, option, tstring); - check_curl_code(result); -} - -void LLCurl::Easy::slist_append(const char* str) -{ - mHeaders = curl_slist_append(mHeaders, str); -} - -size_t curlReadCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - S32 startpos = easy->getInput().tellg(); - easy->getInput().seekg(0, std::ios::end); - S32 endpos = easy->getInput().tellg(); - easy->getInput().seekg(startpos, std::ios::beg); - S32 maxn = endpos - startpos; - n = llmin(n, maxn); - easy->getInput().read((char*)data, n); - - return n; -} - -size_t curlWriteCallback(char* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - S32 n = size * nmemb; - easy->getOutput()->append(easy->getChannels().in(), (const U8*)data, n); - - return n; -} - -size_t curlHeaderCallback(void* data, size_t size, size_t nmemb, void* user_data) -{ - LLCurl::Easy* easy = (LLCurl::Easy*)user_data; - - size_t n = size * nmemb; - easy->getHeaderOutput().write((const char*)data, n); - - return n; -} - -void LLCurl::Easy::prepRequest(const std::string& url, - const std::vector& headers, - ResponderPtr responder, S32 time_out, bool post) -{ - resetState(); - - if (post) setoptString(CURLOPT_ENCODING, ""); - - //setopt(CURLOPT_VERBOSE, 1); // useful for debugging - setopt(CURLOPT_NOSIGNAL, 1); - - // Set the CURL options for either Socks or HTTP proxy - LLProxy::getInstance()->applyProxySettings(this); - - mOutput.reset(new LLBufferArray); - mOutput->setThreaded(true); - setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback); - setopt(CURLOPT_WRITEDATA, (void*)this); - - setopt(CURLOPT_READFUNCTION, (void*)&curlReadCallback); - setopt(CURLOPT_READDATA, (void*)this); - - setopt(CURLOPT_HEADERFUNCTION, (void*)&curlHeaderCallback); - setopt(CURLOPT_HEADERDATA, (void*)this); - - // Allow up to five redirects - if (responder && responder->followRedir()) - { - setopt(CURLOPT_FOLLOWLOCATION, 1); - setopt(CURLOPT_MAXREDIRS, MAX_REDIRECTS); - } - - setErrorBuffer(); - setCA(); - - setopt(CURLOPT_SSL_VERIFYPEER, true); - - //don't verify host name so urls with scrubbed host names will work (improves DNS performance) - setopt(CURLOPT_SSL_VERIFYHOST, 0); - setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); - - setoptString(CURLOPT_URL, url); - - mResponder = responder; - - if (!post) - { - slist_append("Connection: keep-alive"); - slist_append("Keep-alive: 300"); - // Accept and other headers - for (std::vector::const_iterator iter = headers.begin(); - iter != headers.end(); ++iter) - { - slist_append((*iter).c_str()); - } - } -} - -//////////////////////////////////////////////////////////////////////////// -LLCurl::Multi::Multi(F32 idle_time_out) - : mQueued(0), - mErrorCount(0), - mState(STATE_READY), - mDead(FALSE), - mValid(TRUE), - mMutexp(NULL), - mDeletionMutexp(NULL), - mEasyMutexp(NULL) -{ - mCurlMultiHandle = LLCurl::newMultiHandle(); - if (!mCurlMultiHandle) - { - llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; - mCurlMultiHandle = LLCurl::newMultiHandle(); - } - - //llassert_always(mCurlMultiHandle); - - if(mCurlMultiHandle) - { - if(LLCurl::getCurlThread()->getThreaded()) - { - mMutexp = new LLMutex; - mDeletionMutexp = new LLMutex; - mEasyMutexp = new LLMutex; - } - LLCurl::getCurlThread()->addMulti(this) ; - - mIdleTimeOut = idle_time_out ; - if(mIdleTimeOut < LLCurl::sCurlRequestTimeOut) - { - mIdleTimeOut = LLCurl::sCurlRequestTimeOut ; - } - - ++gCurlMultiCount; - } -} - -LLCurl::Multi::~Multi() -{ - cleanup(true); - - delete mDeletionMutexp ; - mDeletionMutexp = NULL ; -} - -void LLCurl::Multi::cleanup(bool deleted) -{ - if(!mCurlMultiHandle) - { - return ; //nothing to clean. - } - llassert_always(deleted || !mValid) ; - - LLMutexLock lock(mDeletionMutexp); - - // Clean up active - for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); - iter != mEasyActiveList.end(); ++iter) - { - Easy* easy = *iter; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - - if(deleted) - { - easy->mResponder = NULL ; //avoid triggering mResponder. - } - delete easy; - } - mEasyActiveList.clear(); - mEasyActiveMap.clear(); - - // Clean up freed - for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer()); - mEasyFreeList.clear(); - - check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle)); - mCurlMultiHandle = NULL ; - - delete mMutexp ; - mMutexp = NULL ; - delete mEasyMutexp ; - mEasyMutexp = NULL ; - - mQueued = 0 ; - mState = STATE_COMPLETED; - - --gCurlMultiCount; - - return ; -} - -void LLCurl::Multi::lock() -{ - if(mMutexp) - { - mMutexp->lock() ; - } -} - -void LLCurl::Multi::unlock() -{ - if(mMutexp) - { - mMutexp->unlock() ; - } -} - -void LLCurl::Multi::markDead() -{ - { - LLMutexLock lock(mDeletionMutexp) ; - - if(mCurlMultiHandle != NULL) - { - mDead = TRUE ; - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; - - return; - } - } - - //not valid, delete it. - delete this; -} - -void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state) -{ - lock() ; - mState = state ; - unlock() ; - - if(mState == STATE_READY) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_NORMAL) ; - } -} - -LLCurl::Multi::ePerformState LLCurl::Multi::getState() -{ - return mState; -} - -bool LLCurl::Multi::isCompleted() -{ - return STATE_COMPLETED == getState() ; -} - -bool LLCurl::Multi::waitToComplete() -{ - if(!isValid()) - { - return true ; - } - - if(!mMutexp) //not threaded - { - doPerform() ; - return true ; - } - - bool completed = (STATE_COMPLETED == mState) ; - if(!completed) - { - LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_HIGH) ; - } - - return completed; -} - -CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) -{ - LLMutexLock lock(mMutexp) ; - - CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue); - return curlmsg; -} - -//return true if dead -bool LLCurl::Multi::doPerform() -{ - LLMutexLock lock(mDeletionMutexp) ; - - bool dead = mDead ; - - if(mDead) - { - setState(STATE_COMPLETED); - mQueued = 0 ; - } - else if(getState() != STATE_COMPLETED) - { - setState(STATE_PERFORMING); - - S32 q = 0; - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count++) - { - LLMutexLock lock(mMutexp) ; - - //WARNING: curl_multi_perform will block for many hundreds of milliseconds - // NEVER call this from the main thread, and NEVER allow the main thread to - // wait on a mutex held by this thread while curl_multi_perform is executing - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - - break; - } - } - - mQueued = q; - setState(STATE_COMPLETED) ; - mIdleTimer.reset() ; - } - else if(!mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it. - { - dead = true ; - } - else if(mValid && mIdleTimer.getElapsedTimeF32() > mIdleTimeOut - 1.f) //idle for too long, mark it invalid. - { - mValid = FALSE ; - } - - return dead ; -} - -S32 LLCurl::Multi::process() -{ - if(!isValid()) - { - return 0 ; - } - - waitToComplete() ; - - if (getState() != STATE_COMPLETED) - { - return 0; - } - - CURLMsg* msg; - int msgs_in_queue; - - S32 processed = 0; - while ((msg = info_read(&msgs_in_queue))) - { - ++processed; - if (msg->msg == CURLMSG_DONE) - { - U32 response = 0; - Easy* easy = NULL ; - - { - LLMutexLock lock(mEasyMutexp) ; - easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle); - if (iter != mEasyActiveMap.end()) - { - easy = iter->second; - } - } - - if(easy) - { - response = easy->report(msg->data.result); - removeEasy(easy); - } - else - { - response = 499; - //*TODO: change to llwarns - llerrs << "cleaned up curl request completed!" << llendl; - } - if (response >= 400) - { - // failure of some sort, inc mErrorCount for debugging and flagging multi for destruction - ++mErrorCount; - } - } - } - - setState(STATE_READY); - - return processed; -} - -LLCurl::Easy* LLCurl::Multi::allocEasy() -{ - Easy* easy = 0; - - if (mEasyFreeList.empty()) - { - easy = Easy::getEasy(); - } - else - { - LLMutexLock lock(mEasyMutexp) ; - easy = *(mEasyFreeList.begin()); - mEasyFreeList.erase(easy); - } - if (easy) - { - LLMutexLock lock(mEasyMutexp) ; - mEasyActiveList.insert(easy); - mEasyActiveMap[easy->getCurlHandle()] = easy; - } - return easy; -} - -bool LLCurl::Multi::addEasy(Easy* easy) -{ - LLMutexLock lock(mMutexp) ; - CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle()); - check_curl_multi_code(mcode); - //if (mcode != CURLM_OK) - //{ - // llwarns << "Curl Error: " << curl_multi_strerror(mcode) << llendl; - // return false; - //} - return true; -} - -void LLCurl::Multi::easyFree(Easy* easy) -{ - if(mEasyMutexp) - { - mEasyMutexp->lock() ; - } - - mEasyActiveList.erase(easy); - mEasyActiveMap.erase(easy->getCurlHandle()); - - if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE) - { - mEasyFreeList.insert(easy); - - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - - easy->resetState(); - } - else - { - if(mEasyMutexp) - { - mEasyMutexp->unlock() ; - } - delete easy; - } -} - -void LLCurl::Multi::removeEasy(Easy* easy) -{ - { - LLMutexLock lock(mMutexp) ; - check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle())); - } - easyFree(easy); -} - -//------------------------------------------------------------ -//LLCurlThread -LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) : - LLQueuedThread::QueuedRequest(handle, LLQueuedThread::PRIORITY_NORMAL, FLAG_AUTO_COMPLETE), - mMulti(multi), - mCurlThread(curl_thread) -{ -} - -LLCurlThread::CurlRequest::~CurlRequest() -{ - if(mMulti) - { - mCurlThread->deleteMulti(mMulti) ; - mMulti = NULL ; - } -} - -bool LLCurlThread::CurlRequest::processRequest() -{ - bool completed = true ; - if(mMulti) - { - completed = mCurlThread->doMultiPerform(mMulti) ; - - if(!completed) - { - setPriority(LLQueuedThread::PRIORITY_LOW) ; - } - } - - return completed ; -} - -void LLCurlThread::CurlRequest::finishRequest(bool completed) -{ - if(mMulti->isDead()) - { - mCurlThread->deleteMulti(mMulti) ; - } - else - { - mCurlThread->cleanupMulti(mMulti) ; //being idle too long, remove the request. - } - - mMulti = NULL ; -} - -LLCurlThread::LLCurlThread(bool threaded) : - LLQueuedThread("curlthread", threaded) -{ -} - -//virtual -LLCurlThread::~LLCurlThread() -{ -} - -S32 LLCurlThread::update(F32 max_time_ms) -{ - return LLQueuedThread::update(max_time_ms); -} - -void LLCurlThread::addMulti(LLCurl::Multi* multi) -{ - multi->mHandle = generateHandle() ; - - CurlRequest* req = new CurlRequest(multi->mHandle, multi, this) ; - - if (!addRequest(req)) - { - llwarns << "curl request added when the thread is quitted" << llendl; - } -} - -void LLCurlThread::killMulti(LLCurl::Multi* multi) -{ - if(!multi) - { - return ; - } - - multi->markDead() ; -} - -//private -bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) -{ - return multi->doPerform() ; -} - -//private -void LLCurlThread::deleteMulti(LLCurl::Multi* multi) -{ - delete multi ; -} - -//private -void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) -{ - multi->cleanup() ; - if(multi->isDead()) //check if marked dead during cleaning up. - { - deleteMulti(multi) ; - } -} - -//------------------------------------------------------------ - -//static -std::string LLCurl::strerror(CURLcode errorcode) -{ - return std::string(curl_easy_strerror(errorcode)); -} - -//////////////////////////////////////////////////////////////////////////// -// For generating a simple request for data -// using one multi and one easy per request - -LLCurlRequest::LLCurlRequest() : - mActiveMulti(NULL), - mActiveRequestCount(0) -{ - mProcessing = FALSE; -} - -LLCurlRequest::~LLCurlRequest() -{ - //stop all Multi handle background threads - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) - { - LLCurl::getCurlThread()->killMulti(*iter) ; - } - mMultiSet.clear() ; -} - -void LLCurlRequest::addMulti() -{ - LLCurl::Multi* multi = new LLCurl::Multi(); - if(!multi->isValid()) - { - LLCurl::getCurlThread()->killMulti(multi) ; - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - return; - } - - mMultiSet.insert(multi); - mActiveMulti = multi; - mActiveRequestCount = 0; + *responder_w = NULL; } LLCurl::Easy* LLCurlRequest::allocEasy() @@ -1078,531 +152,3 @@ LLCurl::Easy* LLCurlRequest::allocEasy() LLCurl::Easy* easy = mActiveMulti->allocEasy(); return easy; } - -bool LLCurlRequest::addEasy(LLCurl::Easy* easy) -{ - llassert_always(mActiveMulti); - - if (mProcessing) - { - llerrs << "Posting to a LLCurlRequest instance from within a responder is not allowed (causes DNS timeouts)." << llendl; - } - bool res = mActiveMulti->addEasy(easy); - return res; -} - -void LLCurlRequest::get(const std::string& url, LLCurl::ResponderPtr responder) -{ - getByteRange(url, headers_t(), 0, -1, responder); -} - -bool LLCurlRequest::getByteRange(const std::string& url, - const headers_t& headers, - S32 offset, S32 length, - LLCurl::ResponderPtr responder) -{ - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder); - easy->setopt(CURLOPT_HTTPGET, 1); - if (length > 0) - { - std::string range = llformat("Range: bytes=%d-%d", offset,offset+length-1); - easy->slist_append(range.c_str()); - } - easy->setHeaders(); - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const LLSD& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - LLSDSerialize::toXML(data, easy->getInput()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append("Content-Type: application/llsd+xml"); - easy->setHeaders(); - - lldebugs << "POSTING: " << bytes << " bytes." << llendl; - bool res = addEasy(easy); - return res; -} - -bool LLCurlRequest::post(const std::string& url, - const headers_t& headers, - const std::string& data, - LLCurl::ResponderPtr responder, S32 time_out) -{ - LLCurl::Easy* easy = allocEasy(); - if (!easy) - { - return false; - } - easy->prepRequest(url, headers, responder, time_out); - - easy->getInput().write(data.data(), data.size()); - S32 bytes = easy->getInput().str().length(); - - easy->setopt(CURLOPT_POST, 1); - easy->setopt(CURLOPT_POSTFIELDS, (void*)NULL); - easy->setopt(CURLOPT_POSTFIELDSIZE, bytes); - - easy->slist_append("Content-Type: application/octet-stream"); - easy->setHeaders(); - - lldebugs << "POSTING: " << bytes << " bytes." << llendl; - bool res = addEasy(easy); - return res; -} - -// Note: call once per frame -S32 LLCurlRequest::process() -{ - S32 res = 0; - - mProcessing = TRUE; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - mMultiSet.erase(curiter) ; - LLCurl::getCurlThread()->killMulti(multi) ; - continue ; - } - - S32 tres = multi->process(); - res += tres; - if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) - { - mMultiSet.erase(curiter); - LLCurl::getCurlThread()->killMulti(multi); - } - } - mProcessing = FALSE; - return res; -} - -S32 LLCurlRequest::getQueued() -{ - S32 queued = 0; - for (curlmulti_set_t::iterator iter = mMultiSet.begin(); - iter != mMultiSet.end(); ) - { - curlmulti_set_t::iterator curiter = iter++; - LLCurl::Multi* multi = *curiter; - - if(!multi->isValid()) - { - if(multi == mActiveMulti) - { - mActiveMulti = NULL ; - mActiveRequestCount = 0 ; - } - LLCurl::getCurlThread()->killMulti(multi); - mMultiSet.erase(curiter) ; - continue ; - } - - queued += multi->mQueued; - if (multi->getState() != LLCurl::Multi::STATE_READY) - { - ++queued; - } - } - return queued; -} - -//////////////////////////////////////////////////////////////////////////// -// For generating one easy request -// associated with a single multi request - -LLCurlEasyRequest::LLCurlEasyRequest() - : mRequestSent(false), - mResultReturned(false) -{ - mMulti = new LLCurl::Multi(); - - if(mMulti->isValid()) - { - mEasy = mMulti->allocEasy(); - if (mEasy) - { - mEasy->setErrorBuffer(); - mEasy->setCA(); - // Set proxy settings if configured to do so. - LLProxy::getInstance()->applyProxySettings(mEasy); - } - } - else - { - LLCurl::getCurlThread()->killMulti(mMulti) ; - mEasy = NULL ; - mMulti = NULL ; - } -} - -LLCurlEasyRequest::~LLCurlEasyRequest() -{ - LLCurl::getCurlThread()->killMulti(mMulti) ; -} - -void LLCurlEasyRequest::setopt(CURLoption option, S32 value) -{ - if (isValid() && mEasy) - { - mEasy->setopt(option, value); - } -} - -void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value) -{ - if (isValid() && mEasy) - { - mEasy->setoptString(option, value); - } -} - -void LLCurlEasyRequest::setPost(char* postdata, S32 size) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_POST, 1); - mEasy->setopt(CURLOPT_POSTFIELDS, postdata); - mEasy->setopt(CURLOPT_POSTFIELDSIZE, size); - } -} - -void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER - } -} - -void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_WRITEDATA, userdata); - } -} - -void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_READDATA, userdata); - } -} - -void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) -{ - if (isValid() && mEasy) - { - mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); - mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); - } -} - -void LLCurlEasyRequest::slist_append(const char* str) -{ - if (isValid() && mEasy) - { - mEasy->slist_append(str); - } -} - -void LLCurlEasyRequest::sendRequest(const std::string& url) -{ - llassert_always(!mRequestSent); - mRequestSent = true; - lldebugs << url << llendl; - if (isValid() && mEasy) - { - mEasy->setHeaders(); - mEasy->setoptString(CURLOPT_URL, url); - mMulti->addEasy(mEasy); - } -} - -void LLCurlEasyRequest::requestComplete() -{ - llassert_always(mRequestSent); - mRequestSent = false; - if (isValid() && mEasy) - { - mMulti->removeEasy(mEasy); - } -} - -// Usage: Call getRestult until it returns false (no more messages) -bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) -{ - if(!isValid()) - { - return false ; - } - if (!mMulti->isCompleted()) - { //we're busy, try again later - return false; - } - mMulti->setState(LLCurl::Multi::STATE_READY) ; - - if (!mEasy) - { - // Special case - we failed to initialize a curl_easy (can happen if too many open files) - // Act as though the request failed to connect - if (mResultReturned) - { - return false; - } - else - { - *result = CURLE_FAILED_INIT; - mResultReturned = true; - return true; - } - } - // In theory, info_read might return a message with a status other than CURLMSG_DONE - // In practice for all messages returned, msg == CURLMSG_DONE - // Ignore other messages just in case - while(1) - { - S32 q; - CURLMsg* curlmsg = info_read(&q, info); - if (curlmsg) - { - if (curlmsg->msg == CURLMSG_DONE) - { - *result = curlmsg->data.result; - return true; - } - // else continue - } - else - { - return false; - } - } -} - -// private -CURLMsg* LLCurlEasyRequest::info_read(S32* q, LLCurl::TransferInfo* info) -{ - if (mEasy) - { - CURLMsg* curlmsg = mMulti->info_read(q); - if (curlmsg && curlmsg->msg == CURLMSG_DONE) - { - if (info) - { - mEasy->getTransferInfo(info); - } - } - return curlmsg; - } - return NULL; -} - -std::string LLCurlEasyRequest::getErrorString() -{ - return isValid() && mEasy ? std::string(mEasy->getErrorBuffer()) : std::string(); -} - -//////////////////////////////////////////////////////////////////////////// - -#if SAFE_SSL -//static -void LLCurl::ssl_locking_callback(int mode, int type, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - { - LLCurl::sSSLMutex[type]->lock(); - } - else - { - LLCurl::sSSLMutex[type]->unlock(); - } -} - -//static -unsigned long LLCurl::ssl_thread_id(void) -{ - return LLThread::currentID(); -} -#endif - -void LLCurl::initClass(F32 curl_reuest_timeout, S32 max_number_handles, bool multi_threaded) -{ - sCurlRequestTimeOut = curl_reuest_timeout ; //seconds - sMaxHandles = max_number_handles ; //max number of handles, (multi handles and easy handles combined). - - // Do not change this "unless you are familiar with and mean to control - // internal operations of libcurl" - // - http://curl.haxx.se/libcurl/c/curl_global_init.html - CURLcode code = curl_global_init(CURL_GLOBAL_ALL); - - check_curl_code(code); - -#if SAFE_SSL - S32 mutex_count = CRYPTO_num_locks(); - for (S32 i=0; iupdate(1)) //finish all tasks - { - break ; - } - } - sCurlThread->shutdown() ; - delete sCurlThread ; - sCurlThread = NULL ; - -#if SAFE_SSL - CRYPTO_set_locking_callback(NULL); - for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer()); -#endif - - for (std::set::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter) - { - CURL* curl = *iter; - LLCurl::deleteEasyHandle(curl); - } - - Easy::sFreeHandles.clear(); - - delete Easy::sHandleMutexp ; - Easy::sHandleMutexp = NULL ; - - delete sHandleMutexp ; - sHandleMutexp = NULL ; - - llassert(Easy::sActiveHandles.empty()); -} - -//static -CURLM* LLCurl::newMultiHandle() -{ - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - llwarns << "no more handles available." << llendl ; - return NULL ; //failed - } - sTotalHandles++; - - CURLM* ret = curl_multi_init() ; - if(!ret) - { - llwarns << "curl_multi_init failed." << llendl ; - } - - return ret ; -} - -//static -CURLMcode LLCurl::deleteMultiHandle(CURLM* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - sTotalHandles-- ; - return curl_multi_cleanup(handle) ; - } - return CURLM_OK ; -} - -//static -CURL* LLCurl::newEasyHandle() -{ - LLMutexLock lock(sHandleMutexp) ; - - if(sTotalHandles + 1 > sMaxHandles) - { - llwarns << "no more handles available." << llendl ; - return NULL ; //failed - } - sTotalHandles++; - - CURL* ret = curl_easy_init() ; - if(!ret) - { - llwarns << "curl_easy_init failed." << llendl ; - } - - return ret ; -} - -//static -void LLCurl::deleteEasyHandle(CURL* handle) -{ - if(handle) - { - LLMutexLock lock(sHandleMutexp) ; - curl_easy_cleanup(handle) ; - sTotalHandles-- ; - } -} - -const unsigned int LLCurl::MAX_REDIRECTS = 5; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -void LLCurlFF::check_easy_code(CURLcode code) -{ - check_curl_code(code); -} -void LLCurlFF::check_multi_code(CURLMcode code) -{ - check_curl_multi_code(code); -} diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index fa8465bfe..32bb6e45f 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -1,457 +1,39 @@ -/** +/** * @file llcurl.h - * @author Zero / Donovan - * @date 2006-10-15 - * @brief A wrapper around libcurl. + * @brief Drop in replacement for old llcurl.h. * - * $LicenseInfo:firstyear=2006&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, + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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$ + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 22/06/2012 + * Initial version, written by Aleric Inglewood @ SL */ - + #ifndef LL_LLCURL_H #define LL_LLCURL_H -#include "linden_common.h" +#include "aicurl.h" -#include -#include -#include - -#include -#include // TODO: remove dependency - -#include "llbuffer.h" -#include "lliopipe.h" -#include "llsd.h" -#include "llthread.h" -#include "llqueuedthread.h" -#include "llframetimer.h" - -class LLMutex; -class LLCurlThread; - -// For whatever reason, this is not typedef'd in curl.h -typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream); - -class LLCurl -{ - LOG_CLASS(LLCurl); - -public: - class Easy; - class Multi; - - struct TransferInfo - { - TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {} - F64 mSizeDownload; - F64 mTotalTime; - F64 mSpeedDownload; - }; - - class Responder - { - //LOG_CLASS(Responder); - public: - - Responder(); - virtual ~Responder(); - - /** - * @brief return true if the status code indicates success. - */ - static bool isGoodStatus(U32 status) - { - return((200 <= status) && (status < 300)); - } - - virtual void errorWithContent( - U32 status, - const std::string& reason, - const LLSD& content); - //< called by completed() on bad status - - virtual void error(U32 status, const std::string& reason); - //< called by default error(status, reason, content) - - virtual void result(const LLSD& content); - //< called by completed for good status codes. - - virtual void completedRaw( - U32 status, - const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); - /**< Override point for clients that may want to use this - class when the response is some other format besides LLSD - */ - - virtual void completed( - U32 status, - const std::string& reason, - const LLSD& content); - /**< The default implemetnation calls - either: - * result(), or - * error() - */ - - // Override to handle parsing of the header only. Note: this is the only place where the contents - // of the header can be parsed. In the ::completed call above only the body is contained in the LLSD. - virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content); - - // Used internally to set the url for debugging later. - void setURL(const std::string& url); - - virtual bool followRedir() - { - return false; - } - - public: /* but not really -- don't touch this */ - U32 mReferenceCount; - - private: - std::string mURL; - }; - typedef boost::intrusive_ptr ResponderPtr; - - - /** - * @ brief Set certificate authority file used to verify HTTPS certs. - */ - static void setCAFile(const std::string& file); - - /** - * @ brief Set certificate authority path used to verify HTTPS certs. - */ - static void setCAPath(const std::string& path); - - /** - * @ brief Return human-readable string describing libcurl version. - */ - static std::string getVersionString(); - - /** - * @ brief Get certificate authority file used to verify HTTPS certs. - */ - static const std::string& getCAFile() { return sCAFile; } - - /** - * @ brief Get certificate authority path used to verify HTTPS certs. - */ - static const std::string& getCAPath() { return sCAPath; } - - /** - * @ brief Initialize LLCurl class - */ - static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false); - - /** - * @ brief Cleanup LLCurl class - */ - static void cleanupClass(); - - /** - * @ brief curl error code -> string - */ - static std::string strerror(CURLcode errorcode); - - // For OpenSSL callbacks - static std::vector sSSLMutex; - - // OpenSSL callbacks - static void ssl_locking_callback(int mode, int type, const char *file, int line); - static unsigned long ssl_thread_id(void); - - static LLCurlThread* getCurlThread() { return sCurlThread ;} - - static CURLM* newMultiHandle() ; - static CURLMcode deleteMultiHandle(CURLM* handle) ; - static CURL* newEasyHandle() ; - static void deleteEasyHandle(CURL* handle) ; - -private: - static std::string sCAPath; - static std::string sCAFile; - static const unsigned int MAX_REDIRECTS; - static LLCurlThread* sCurlThread; - - static LLMutex* sHandleMutexp ; - static S32 sTotalHandles ; - static S32 sMaxHandles; -public: - static bool sNotQuitting; - static F32 sCurlRequestTimeOut; -}; - -class LLCurl::Easy -{ - LOG_CLASS(Easy); - -private: - Easy(); - -public: - static Easy* getEasy(); - ~Easy(); - - CURL* getCurlHandle() const { return mCurlEasyHandle; } - - void setErrorBuffer(); - void setCA(); - - void setopt(CURLoption option, S32 value); - // These assume the setter does not free value! - void setopt(CURLoption option, void* value); - void setopt(CURLoption option, char* value); - // Copies the string so that it is guaranteed to stick around - void setoptString(CURLoption option, const std::string& value); - - void slist_append(const char* str); - void setHeaders(); - - U32 report(CURLcode); - void getTransferInfo(LLCurl::TransferInfo* info); - - void prepRequest(const std::string& url, const std::vector& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false); - - const char* getErrorBuffer(); - - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } - const LLChannelDescriptors& getChannels() { return mChannels; } - - void resetState(); - - static CURL* allocEasyHandle(); - static void releaseEasyHandle(CURL* handle); - -private: - friend class LLCurl; - friend class LLCurl::Multi; - - CURL* mCurlEasyHandle; - struct curl_slist* mHeaders; - - std::stringstream mRequest; - LLChannelDescriptors mChannels; - LLIOPipe::buffer_ptr_t mOutput; - std::stringstream mInput; - std::stringstream mHeaderOutput; - char mErrorBuffer[CURL_ERROR_SIZE]; - - // Note: char*'s not strings since we pass pointers to curl - std::vector mStrings; - - LLCurl::ResponderPtr mResponder; - - static std::set sFreeHandles; - static std::set sActiveHandles; - static LLMutex* sHandleMutexp ; -}; - -class LLCurl::Multi -{ - LOG_CLASS(Multi); - - friend class LLCurlThread ; - -private: - ~Multi(); - - void markDead() ; - bool doPerform(); - -public: - - typedef enum - { - STATE_READY=0, - STATE_PERFORMING=1, - STATE_COMPLETED=2 - } ePerformState; - - Multi(F32 idle_time_out = 0.f); - - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - void removeEasy(LLCurl::Easy* easy); - - void lock() ; - void unlock() ; - - void setState(ePerformState state) ; - ePerformState getState() ; - - bool isCompleted() ; - bool isValid() {return mCurlMultiHandle != NULL && mValid;} - bool isDead() {return mDead;} - - bool waitToComplete() ; - - S32 process(); - - CURLMsg* info_read(S32* msgs_in_queue); - - S32 mQueued; - S32 mErrorCount; - -private: - void easyFree(LLCurl::Easy*); - void cleanup(bool deleted = false); - - CURLM* mCurlMultiHandle; - - typedef std::set easy_active_list_t; - easy_active_list_t mEasyActiveList; - typedef std::map easy_active_map_t; - easy_active_map_t mEasyActiveMap; - typedef std::set easy_free_list_t; - easy_free_list_t mEasyFreeList; - - LLQueuedThread::handle_t mHandle ; - ePerformState mState; - - BOOL mDead ; - BOOL mValid ; - LLMutex* mMutexp ; - LLMutex* mDeletionMutexp ; - LLMutex* mEasyMutexp ; - LLFrameTimer mIdleTimer ; - F32 mIdleTimeOut; -}; - -class LLCurlThread : public LLQueuedThread -{ -public: - - class CurlRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~CurlRequest(); // use deleteRequest() - - public: - CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread); - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - - private: - // input - LLCurl::Multi* mMulti; - LLCurlThread* mCurlThread; - }; - friend class CurlRequest; - -public: - LLCurlThread(bool threaded = true) ; - virtual ~LLCurlThread() ; - - S32 update(F32 max_time_ms); - - void addMulti(LLCurl::Multi* multi) ; - void killMulti(LLCurl::Multi* multi) ; - -private: - bool doMultiPerform(LLCurl::Multi* multi) ; - void deleteMulti(LLCurl::Multi* multi) ; - void cleanupMulti(LLCurl::Multi* multi) ; -} ; - -//namespace boost -//{ - void intrusive_ptr_add_ref(LLCurl::Responder* p); - void intrusive_ptr_release(LLCurl::Responder* p); -//}; - - -class LLCurlRequest -{ -public: - typedef std::vector headers_t; - - LLCurlRequest(); - ~LLCurlRequest(); - - void get(const std::string& url, LLCurl::ResponderPtr responder); - bool getByteRange(const std::string& url, const headers_t& headers, S32 offset, S32 length, LLCurl::ResponderPtr responder); - bool post(const std::string& url, const headers_t& headers, const LLSD& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - bool post(const std::string& url, const headers_t& headers, const std::string& data, LLCurl::ResponderPtr responder, S32 time_out = 0); - - S32 process(); - S32 getQueued(); - -private: - void addMulti(); - LLCurl::Easy* allocEasy(); - bool addEasy(LLCurl::Easy* easy); - -private: - typedef std::set curlmulti_set_t; - curlmulti_set_t mMultiSet; - LLCurl::Multi* mActiveMulti; - S32 mActiveRequestCount; - BOOL mProcessing; -}; - -class LLCurlEasyRequest -{ -public: - LLCurlEasyRequest(); - ~LLCurlEasyRequest(); - void setopt(CURLoption option, S32 value); - void setoptString(CURLoption option, const std::string& value); - void setPost(char* postdata, S32 size); - void setHeaderCallback(curl_header_callback callback, void* userdata); - void setWriteCallback(curl_write_callback callback, void* userdata); - void setReadCallback(curl_read_callback callback, void* userdata); - void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); - void slist_append(const char* str); - void sendRequest(const std::string& url); - void requestComplete(); - bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); - std::string getErrorString(); - bool isCompleted() {return mMulti->isCompleted() ;} - bool wait() { return mMulti->waitToComplete(); } - bool isValid() {return mMulti && mMulti->isValid(); } - - LLCurl::Easy* getEasy() const { return mEasy; } - -private: - CURLMsg* info_read(S32* queue, LLCurl::TransferInfo* info); - -private: - LLCurl::Multi* mMulti; - LLCurl::Easy* mEasy; - bool mRequestSent; - bool mResultReturned; -}; - -// Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace. -namespace LLCurlFF -{ - void check_easy_code(CURLcode code); - void check_multi_code(CURLMcode code); -} +// Map interface to old LLCurl names so this can be used as a drop-in replacement. +namespace LLCurl = AICurlInterface; #endif // LL_LLCURL_H diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 037688906..355eebb77 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -220,17 +220,28 @@ static void request( const LLSD& headers = LLSD() ) { + if (responder) + { + // For possible debug output from within the responder. + responder->setURL(url); + } + if (!LLHTTPClient::hasPump()) { - responder->completed(U32_MAX, "No pump", LLSD()); + responder->fatalError("No pump"); return; } LLPumpIO::chain_t chain; - LLURLRequest* req = new LLURLRequest(method, url); - if(!req->isValid())//failed + LLURLRequest* req; + try { - delete req ; + req = new LLURLRequest(method, url); + } + catch(AICurlNoEasyHandle& error) + { + llwarns << "Failed to create LLURLRequest: " << error.what() << llendl; + // This is what the old LL code did: no recovery whatsoever (but also no leaks or crash). return ; } @@ -282,11 +293,6 @@ static void request( } } - if (responder) - { - responder->setURL(url); - } - req->setCallback(new LLHTTPClientURLAdaptor(responder)); if (method == LLURLRequest::HTTP_POST && gMessageSystem) @@ -333,7 +339,7 @@ void LLHTTPClient::getByteRange( std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); headers["Range"] = range; } - request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); + request(url, LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); } void LLHTTPClient::head( @@ -372,12 +378,12 @@ class LLHTTPBuffer public: LLHTTPBuffer() { } - static size_t curl_write( void *ptr, size_t size, size_t nmemb, void *user_data) + static size_t curl_write(char* ptr, size_t size, size_t nmemb, void* user_data) { LLHTTPBuffer* self = (LLHTTPBuffer*)user_data; size_t bytes = (size * nmemb); - self->mBuffer.append((char*)ptr,bytes); + self->mBuffer.append(ptr,bytes); return nmemb; } @@ -428,104 +434,91 @@ static LLSD blocking_request( ) { lldebugs << "blockingRequest of " << url << llendl; - char curl_error_buffer[CURL_ERROR_SIZE] = "\0"; - CURL* curlp = LLCurl::newEasyHandle(); - llassert_always(curlp != NULL) ; - LLHTTPBuffer http_buffer; - std::string body_str; - - // other request method checks root cert first, we skip? + S32 http_status = 499; + LLSD response = LLSD::emptyMap(); - // Apply configured proxy settings - LLProxy::getInstance()->applyProxySettings(curlp); - - // * Set curl handle options - curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts - curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function. - curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write); - curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer); - curl_easy_setopt(curlp, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer); - - // * Setup headers (don't forget to free them after the call!) - curl_slist* headers_list = NULL; - if (headers.isMap()) + try { - LLSD::map_const_iterator iter = headers.beginMap(); - LLSD::map_const_iterator end = headers.endMap(); - for (; iter != end; ++iter) + AICurlEasyRequest easy_request(false); + AICurlEasyRequest_wat curlEasyRequest_w(*easy_request); + + LLHTTPBuffer http_buffer; + std::string body_str; + + // * Set curl handle options + curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function. + curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer); + + // * Setup headers. + if (headers.isMap()) { - std::ostringstream header; - header << iter->first << ": " << iter->second.asString() ; - lldebugs << "header = " << header.str() << llendl; - headers_list = curl_slist_append(headers_list, header.str().c_str()); + LLSD::map_const_iterator iter = headers.beginMap(); + LLSD::map_const_iterator end = headers.endMap(); + for (; iter != end; ++iter) + { + std::ostringstream header; + header << iter->first << ": " << iter->second.asString() ; + lldebugs << "header = " << header.str() << llendl; + curlEasyRequest_w->addHeader(header.str().c_str()); + } + } + + // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) + if (method == LLURLRequest::HTTP_GET) + { + curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); + } + else if (method == LLURLRequest::HTTP_POST) + { + curlEasyRequest_w->setopt(CURLOPT_POST, 1); + //serialize to ostr then copy to str - need to because ostr ptr is unstable :( + std::ostringstream ostr; + LLSDSerialize::toXML(body, ostr); + body_str = ostr.str(); + curlEasyRequest_w->setopt(CURLOPT_POSTFIELDS, body_str.c_str()); + //copied from PHP libs, correct? + curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml"); + + // copied from llurlrequest.cpp + // it appears that apache2.2.3 or django in etch is busted. If + // we do not clear the expect header, we get a 500. May be + // limited to django/mod_wsgi. + curlEasyRequest_w->addHeader("Expect:"); + } + + // * Do the action using curl, handle results + lldebugs << "HTTP body: " << body_str << llendl; + curlEasyRequest_w->addHeader("Accept: application/llsd+xml"); + curlEasyRequest_w->finalizeRequest(url); + + S32 curl_success = curlEasyRequest_w->perform(); + curlEasyRequest_w->getinfo(CURLINFO_RESPONSE_CODE, &http_status); + // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits, + if ( http_status != 404 && (http_status != 200 || curl_success != 0) ) + { + // We expect 404s, don't spam for them. + llwarns << "CURL REQ URL: " << url << llendl; + llwarns << "CURL REQ METHOD TYPE: " << method << llendl; + llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; + llwarns << "CURL REQ BODY: " << body_str << llendl; + llwarns << "CURL HTTP_STATUS: " << http_status << llendl; + llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl; + llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; + response["body"] = http_buffer.asString(); + } + else + { + response["body"] = http_buffer.asLLSD(); + lldebugs << "CURL response: " << http_buffer.asString() << llendl; } } - - // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) - if (method == LLURLRequest::HTTP_GET) + catch(AICurlNoEasyHandle const& error) { - curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1); - } - else if (method == LLURLRequest::HTTP_POST) - { - curl_easy_setopt(curlp, CURLOPT_POST, 1); - //serialize to ostr then copy to str - need to because ostr ptr is unstable :( - std::ostringstream ostr; - LLSDSerialize::toXML(body, ostr); - body_str = ostr.str(); - curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str()); - //copied from PHP libs, correct? - headers_list = curl_slist_append(headers_list, "Content-Type: application/llsd+xml"); - - // copied from llurlrequest.cpp - // it appears that apache2.2.3 or django in etch is busted. If - // we do not clear the expect header, we get a 500. May be - // limited to django/mod_wsgi. - headers_list = curl_slist_append(headers_list, "Expect:"); - } - - // * Do the action using curl, handle results - lldebugs << "HTTP body: " << body_str << llendl; - headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml"); - CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list); - if ( curl_result != CURLE_OK ) - { - llinfos << "Curl is hosed - can't add headers" << llendl; + response["body"] = error.what(); } - LLSD response = LLSD::emptyMap(); - S32 curl_success = curl_easy_perform(curlp); - S32 http_status = 499; - curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status); response["status"] = http_status; - // if we get a non-404 and it's not a 200 OR maybe it is but you have error bits, - if ( http_status != 404 && (http_status != 200 || curl_success != 0) ) - { - // We expect 404s, don't spam for them. - llwarns << "CURL REQ URL: " << url << llendl; - llwarns << "CURL REQ METHOD TYPE: " << method << llendl; - llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; - llwarns << "CURL REQ BODY: " << body_str << llendl; - llwarns << "CURL HTTP_STATUS: " << http_status << llendl; - llwarns << "CURL ERROR: " << curl_error_buffer << llendl; - llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; - response["body"] = http_buffer.asString(); - } - else - { - response["body"] = http_buffer.asLLSD(); - lldebugs << "CURL response: " << http_buffer.asString() << llendl; - } - - if(headers_list) - { // free the header list - curl_slist_free_all(headers_list); - } - - // * Cleanup - LLCurl::deleteEasyHandle(curlp); return response; } diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index dda7d550f..39a6498d8 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -55,7 +55,9 @@ public: typedef LLCurl::Responder Responder; typedef LLCurl::ResponderPtr ResponderPtr; - + // The default actually already ignores responses. + class ResponderIgnore : public Responder { }; + /** @name non-blocking API */ //@{ static void head( diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 4a7d326c0..3db851001 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -406,16 +406,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -void LLProxy::applyProxySettings(LLCurlEasyRequest* handle) -{ - applyProxySettings(handle->getEasy()); -} - -void LLProxy::applyProxySettings(LLCurl::Easy* handle) -{ - applyProxySettings(handle->getCurlHandle()); -} - /** * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. * @@ -425,9 +415,9 @@ void LLProxy::applyProxySettings(LLCurl::Easy* handle) * When the HTTP proxy is enabled, the proxy mutex will * be locked every time this method is called. * - * @param handle A pointer to a valid CURL request, before it has been performed. + * @param curlEasyRequest_w An already locked curl easy handle, before it has been performed. */ -void LLProxy::applyProxySettings(CURL* handle) +void LLProxy::applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w) { // Do a faster unlocked check to see if we are supposed to proxy. if (mHTTPProxyEnabled) @@ -437,21 +427,21 @@ void LLProxy::applyProxySettings(CURL* handle) // Now test again to verify that the proxy wasn't disabled between the first check and the lock. if (mHTTPProxyEnabled) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str())); - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort())); + curlEasyRequest_w->setopt(CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()); + curlEasyRequest_w->setopt(CURLOPT_PROXYPORT, mHTTPProxy.getPort()); if (mProxyType == LLPROXY_SOCKS) { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5)); + curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); if (mAuthMethodSelected == METHOD_PASSWORD) { std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str())); + curlEasyRequest_w->setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); } } else { - LLCurlFF::check_easy_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP)); + curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } } } diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index a91937054..94f031fc3 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -206,15 +206,7 @@ enum LLSocks5AuthType * The implementation of HTTP proxying is handled by libcurl. LLProxy * is responsible for managing the HTTP proxy options and provides a * thread-safe method to apply those options to a curl request - * (LLProxy::applyProxySettings()). This method is overloaded - * to accommodate the various abstraction libcurl layers that exist - * throughout the viewer (LLCurlEasyRequest, LLCurl::Easy, and CURL). - * - * If you are working with LLCurl or LLCurlEasyRequest objects, - * the configured proxy settings will be applied in the constructors - * of those request handles. If you are working with CURL objects - * directly, you will need to pass the handle of the request to - * applyProxySettings() before issuing the request. + * (LLProxy::applyProxySettings()). * * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. @@ -251,9 +243,7 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. - void applyProxySettings(CURL* handle); - void applyProxySettings(LLCurl::Easy* handle); - void applyProxySettings(LLCurlEasyRequest* handle); + void applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w); // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); diff --git a/indra/llmessage/llsdmessage.cpp b/indra/llmessage/llsdmessage.cpp index 9148c9dd1..95ed03e66 100644 --- a/indra/llmessage/llsdmessage.cpp +++ b/indra/llmessage/llsdmessage.cpp @@ -151,11 +151,11 @@ bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success) { if (success) { - mResponder->result(payload); + mResponder->pubResult(payload); } else { - mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]); + mResponder->pubErrorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]); } /*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/ diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h index 0cecf4f68..80fa82dcc 100644 --- a/indra/llmessage/llsdrpcclient.h +++ b/indra/llmessage/llsdrpcclient.h @@ -240,11 +240,14 @@ public: virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const { lldebugs << "LLSDRPCClientFactory::build" << llendl; - LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST)); - if(!http->isValid()) + LLURLRequest* http; + try { - llwarns << "Creating LLURLRequest failed." << llendl ; - delete http; + http = new LLURLRequest(LLURLRequest::HTTP_POST); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ; return false; } @@ -291,11 +294,14 @@ public: { lldebugs << "LLXMLSDRPCClientFactory::build" << llendl; - LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST)); - if(!http->isValid()) + LLURLRequest* http; + try { - llwarns << "Creating LLURLRequest failed." << llendl ; - delete http; + http = new LLURLRequest(LLURLRequest::HTTP_POST); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ; return false ; } LLIOPipe::ptr_t service(new Client); diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index f5e08f393..97a8e48d9 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -29,6 +29,10 @@ #include "linden_common.h" #include "llurlrequest.h" +#ifdef CWDEBUG +#include +#endif + #include #include #include @@ -52,9 +56,7 @@ const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); - - +static size_t headerCallback(char* data, size_t size, size_t nmemb, void* user); /** * class LLURLRequestDetail @@ -65,7 +67,7 @@ public: LLURLRequestDetail(); ~LLURLRequestDetail(); std::string mURL; - LLCurlEasyRequest* mCurlRequest; + AICurlEasyRequest mCurlEasyRequest; LLIOPipe::buffer_ptr_t mResponseBuffer; LLChannelDescriptors mChannels; U8* mLastRead; @@ -76,36 +78,28 @@ public: }; LLURLRequestDetail::LLURLRequestDetail() : - mCurlRequest(NULL), + mCurlEasyRequest(false), mLastRead(NULL), mBodyLimit(0), mByteAccumulator(0), mIsBodyLimitSet(false), mSSLVerifyCallback(NULL) { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mCurlRequest = new LLCurlEasyRequest(); - - if(!mCurlRequest->isValid()) //failed. - { - delete mCurlRequest ; - mCurlRequest = NULL ; - } } LLURLRequestDetail::~LLURLRequestDetail() { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - delete mCurlRequest; mLastRead = NULL; } void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) { + LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mDetail->mSSLVerifyCallback = callback; - mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, true); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, 2); } @@ -159,6 +153,7 @@ LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) : mAction(action) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + // This might throw AICurlNoEasyHandle. initialize(); } @@ -168,6 +163,7 @@ LLURLRequest::LLURLRequest( mAction(action) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + // This might throw AICurlNoEasyHandle. initialize(); setURL(url); } @@ -175,13 +171,16 @@ LLURLRequest::LLURLRequest( LLURLRequest::~LLURLRequest() { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); + { + AICurlEasyRequest_wat curl_easy_request_w(*mDetail->mCurlEasyRequest); + curl_easy_request_w->revokeCallbacks(); + curl_easy_request_w->send_events_to(NULL); + } delete mDetail; - mDetail = NULL ; } void LLURLRequest::setURL(const std::string& url) { - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mDetail->mURL = url; } @@ -193,7 +192,8 @@ std::string LLURLRequest::getURL() const void LLURLRequest::addHeader(const char* header) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mDetail->mCurlRequest->slist_append(header); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->addHeader(header); } void LLURLRequest::setBodyLimit(U32 size) @@ -206,7 +206,8 @@ void LLURLRequest::setCallback(LLURLRequestComplete* callback) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mCompletionCallback = callback; - mDetail->mCurlRequest->setHeaderCallback(&headerCallback, (void*)callback); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setHeaderCallback(&headerCallback, (void*)callback); } // Added to mitigate the effect of libcurl looking @@ -242,30 +243,28 @@ void LLURLRequest::useProxy(bool use_proxy) lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = \"" << env_proxy << "\"" << llendl; - if (use_proxy) - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, env_proxy); - } - else - { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, ""); - } + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_PROXY, use_proxy ? env_proxy : std::string("")); } void LLURLRequest::useProxy(const std::string &proxy) { - mDetail->mCurlRequest->setoptString(CURLOPT_PROXY, proxy); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_PROXY, proxy); } void LLURLRequest::allowCookies() { - mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, ""); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setoptString(CURLOPT_COOKIEFILE, ""); } //virtual bool LLURLRequest::isValid() { - return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); + //FIXME - wtf is with this isValid? + //return mDetail->mCurlRequest->isValid(); + return true; } // virtual @@ -294,6 +293,19 @@ LLIOPipe::EStatus LLURLRequest::handleError( return status; } +void LLURLRequest::added_to_multi_handle(AICurlEasyRequest_wat&) +{ +} + +void LLURLRequest::finished(AICurlEasyRequest_wat&) +{ +} + +void LLURLRequest::removed_from_multi_handle(AICurlEasyRequest_wat&) +{ + mRemoved = true; +} + static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST("URL Request"); // virtual @@ -310,7 +322,7 @@ LLIOPipe::EStatus LLURLRequest::process_impl( //llinfos << "LLURLRequest::process_impl()" << llendl; if (!buffer) return STATUS_ERROR; - // we're still waiting or prcessing, check how many + // we're still waiting or processing, check how many // bytes we have accumulated. const S32 MIN_ACCUMULATION = 100000; if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) @@ -354,44 +366,36 @@ LLIOPipe::EStatus LLURLRequest::process_impl( { return STATUS_ERROR; } + mRemoved = false; mState = STATE_WAITING_FOR_RESPONSE; + mDetail->mCurlEasyRequest.addRequest(); // Add easy handle to multi handle. - // *FIX: Maybe we should just go to the next state now... return STATUS_BREAK; } case STATE_WAITING_FOR_RESPONSE: case STATE_PROCESSING_RESPONSE: { - PUMP_DEBUG; - LLIOPipe::EStatus status = STATUS_BREAK; - static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform"); + if (!mRemoved) // Not removed from multi handle yet? { - LLFastTimer t(FTM_URL_PERFORM); - if(!mDetail->mCurlRequest->wait()) - { - return status ; - } + // Easy handle is still being processed. + return STATUS_BREAK; } + // Curl thread finished with this easy handle. + mState = STATE_CURL_FINISHED; + } + case STATE_CURL_FINISHED: + { + PUMP_DEBUG; + LLIOPipe::EStatus status = STATUS_NO_CONNECTION; // Catch-all failure code. - while(1) + // Left braces in order not to change indentation. { CURLcode result; static LLFastTimer::DeclareTimer FTM_PROCESS_URL_REQUEST_GET_RESULT("Get Result"); - bool newmsg = false; - { - LLFastTimer t(FTM_PROCESS_URL_REQUEST_GET_RESULT); - newmsg = mDetail->mCurlRequest->getResult(&result); - } + AICurlEasyRequest_wat(*mDetail->mCurlEasyRequest)->getResult(&result); - if(!newmsg) - { - // keep processing - break; - } - - mState = STATE_HAVE_RESPONSE; context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; @@ -423,6 +427,7 @@ LLIOPipe::EStatus LLURLRequest::process_impl( } mCompletionCallback = NULL; } + status = STATUS_BREAK; // This is what the old code returned. Does it make sense? break; case CURLE_FAILED_INIT: case CURLE_COULDNT_CONNECT: @@ -464,16 +469,15 @@ void LLURLRequest::initialize() { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mState = STATE_INITIALIZED; + // This might throw AICurlNoEasyHandle. mDetail = new LLURLRequestDetail; - if(!isValid()) { - return ; + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setWriteCallback(&downCallback, (void*)this); + curlEasyRequest_w->setReadCallback(&upCallback, (void*)this); } - mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this); - mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this); mRequestTransferedBytes = 0; mResponseTransferedBytes = 0; } @@ -488,70 +492,74 @@ bool LLURLRequest::configure() S32 bytes = mDetail->mResponseBuffer->countAfter( mDetail->mChannels.in(), NULL); - switch(mAction) { - case HTTP_HEAD: - mDetail->mCurlRequest->setopt(CURLOPT_HEADER, 1); - mDetail->mCurlRequest->setopt(CURLOPT_NOBODY, 1); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); - rv = true; - break; - case HTTP_GET: - mDetail->mCurlRequest->setopt(CURLOPT_HTTPGET, 1); - mDetail->mCurlRequest->setopt(CURLOPT_FOLLOWLOCATION, 1); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + switch(mAction) + { + case HTTP_HEAD: + curlEasyRequest_w->setopt(CURLOPT_HEADER, 1); + curlEasyRequest_w->setopt(CURLOPT_NOBODY, 1); + curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); + rv = true; + break; + case HTTP_GET: + curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); + curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; + // Set Accept-Encoding to allow response compression + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + rv = true; + break; - case HTTP_PUT: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader("Expect:"); + case HTTP_PUT: + // Disable the expect http 1.1 extension. POST and PUT default + // to turning this on, and I am not too sure what it means. + addHeader("Expect:"); - mDetail->mCurlRequest->setopt(CURLOPT_UPLOAD, 1); - mDetail->mCurlRequest->setopt(CURLOPT_INFILESIZE, bytes); - rv = true; - break; + curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1); + curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes); + rv = true; + break; - case HTTP_POST: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader("Expect:"); + case HTTP_POST: + // Disable the expect http 1.1 extension. POST and PUT default + // to turning this on, and I am not too sure what it means. + addHeader("Expect:"); - // Disable the content type http header. - // *FIX: what should it be? - addHeader("Content-Type:"); + // Disable the content type http header. + // *FIX: what should it be? + addHeader("Content-Type:"); - // Set the handle for an http post - mDetail->mCurlRequest->setPost(NULL, bytes); + // Set the handle for an http post + curlEasyRequest_w->setPost(NULL, bytes); - // Set Accept-Encoding to allow response compression - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - rv = true; - break; + // Set Accept-Encoding to allow response compression + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + rv = true; + break; - case HTTP_DELETE: - // Set the handle for an http post - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); - rv = true; - break; + case HTTP_DELETE: + // Set the handle for an http post + curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "DELETE"); + rv = true; + break; - case HTTP_MOVE: - // Set the handle for an http post - mDetail->mCurlRequest->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); - // *NOTE: should we check for the Destination header? - rv = true; - break; + case HTTP_MOVE: + // Set the handle for an http post + curlEasyRequest_w->setoptString(CURLOPT_CUSTOMREQUEST, "MOVE"); + // *NOTE: should we check for the Destination header? + rv = true; + break; - default: - llwarns << "Unhandled URLRequest action: " << mAction << llendl; - break; - } - if(rv) - { - mDetail->mCurlRequest->sendRequest(mDetail->mURL); + default: + llwarns << "Unhandled URLRequest action: " << mAction << llendl; + break; + } + if(rv) + { + curlEasyRequest_w->finalizeRequest(mDetail->mURL); + curlEasyRequest_w->send_events_to(this); + } } return rv; } @@ -615,9 +623,8 @@ size_t LLURLRequest::upCallback( return bytes; } -static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user) +static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void* user) { - const char* header_line = (const char*)data; size_t header_len = size * nmemb; LLURLRequestComplete* complete = (LLURLRequestComplete*)user; diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 1ba51a5ac..6cfe83a5e 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -66,7 +66,7 @@ typedef struct x509_store_ctx_st X509_STORE_CTX; * worth the time and effort to eventually port this to a raw client * socket. */ -class LLURLRequest : public LLIOPipe +class LLURLRequest : public LLIOPipe, protected AICurlEasyHandleEvents { LOG_CLASS(LLURLRequest); public: @@ -217,6 +217,7 @@ protected: STATE_INITIALIZED, STATE_WAITING_FOR_RESPONSE, STATE_PROCESSING_RESPONSE, + STATE_CURL_FINISHED, STATE_HAVE_RESPONSE, }; EState mState; @@ -228,6 +229,14 @@ protected: static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); + // mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic. + // Set to false before adding curl request and then only tested. + // Reset in removed_from_multi_handle (by another thread), this is thread-safe. + bool mRemoved; + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat&); + /*virtual*/ void finished(AICurlEasyRequest_wat&); + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat&); + private: /** * @brief Initialize the object. Called during construction. diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 83125c7fd..bbb8b5eb4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -151,6 +151,7 @@ set(viewer_SOURCE_FILES llconfirmationmanager.cpp llconsole.cpp llcontainerview.cpp + llcurlrequest.cpp llcurrencyuimanager.cpp llcylinder.cpp lldebugmessagebox.cpp @@ -646,6 +647,7 @@ set(viewer_HEADER_FILES llconfirmationmanager.h llconsole.h llcontainerview.h + llcurlrequest.h llcurrencyuimanager.h llcylinder.h lldebugmessagebox.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3af73986a..cdabf2221 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4016,17 +4016,6 @@ Value 120.0 - CurlUseMultipleThreads - - Comment - Use background threads for executing curl_multi_perform (requires restart) - Persist - 1 - Type - Boolean - Value - 1 - Cursor3D Comment diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index 7b64b35d8..17875f27e 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -255,7 +255,16 @@ static void request(const std::string &url, } LLPumpIO::chain_t chain; - LLURLRequest *req = new LLURLRequest(method, url); + LLURLRequest *req; + try + { + req = new LLURLRequest(method, url); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Failed to create LLURLRequest: " << error.what() << llendl; + return; + } req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); /* @@ -324,10 +333,11 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result) char curlErrorBuffer[CURL_ERROR_SIZE]; CURL* curlp = curl_easy_init(); + llassert_always(curlp); curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5); // seconds - curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); + curl_easy_setopt(curlp, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str()); curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, curlWrite); curl_easy_setopt(curlp, CURLOPT_WRITEDATA, result); @@ -337,7 +347,7 @@ int HippoRestRequest::getBlocking(const std::string &url, std::string *result) *result = ""; S32 curlSuccess = curl_easy_perform(curlp); - S32 httpStatus = 499; + long httpStatus = 499L; // curl_easy_getinfo demands pointer to long. curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &httpStatus); if (curlSuccess != 0) { diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index d0ba0a140..d00b1f979 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2370,7 +2370,7 @@ bool LLAgent::sendMaturityPreferenceToServer(int preferredMaturity) body["access_prefs"] = access_prefs; llinfos << "Sending access prefs update to " << (access_prefs["max"].asString()) << " via capability to: " << url << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); // Ignore response + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); // Ignore response return true; } return false; diff --git a/indra/newview/llagentlanguage.cpp b/indra/newview/llagentlanguage.cpp index 04e1228c7..4dbc7eb70 100644 --- a/indra/newview/llagentlanguage.cpp +++ b/indra/newview/llagentlanguage.cpp @@ -61,7 +61,7 @@ bool LLAgentLanguage::update() body["language"] = language; body["language_is_public"] = gSavedSettings.getBOOL("LanguageIsPublic"); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } return true; } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ad9b7d8ed..41aba1942 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -611,6 +611,9 @@ bool LLAppViewer::init() initLogging(); + // Curl must be initialized before any thread is running. + AICurlInterface::initCurl(); + // Logging is initialized. Now it's safe to start the error thread. startErrorThread(); @@ -635,12 +638,6 @@ bool LLAppViewer::init() LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ; mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); - // *NOTE:Mani - LLCurl::initClass is not thread safe. - // Called before threads are created. - LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), - gSavedSettings.getS32("CurlMaximumNumberOfHandles"), - gSavedSettings.getBOOL("CurlUseMultipleThreads")); - LL_INFOS("InitInfo") << "LLCurl initialized." << LL_ENDL ; initThreads(); LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ; @@ -1767,10 +1764,9 @@ bool LLAppViewer::cleanup() end_messaging_system(); llinfos << "Message system deleted." << llendflush; - LLUserAuth::getInstance()->reset(); //reset before LLCurl::cleanupClass, else LLCURL::sHandleMutex == NULL - // *NOTE:Mani - The following call is not thread safe. - LLCurl::cleanupClass(); - llinfos << "LLCurl cleaned up." << llendflush; + LLUserAuth::getInstance()->reset(); // Reset before AICurlInterface::cleanupCurl, else LLCURL::sHandleMutex == NULL + LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads. + AICurlInterface::cleanupCurl(); // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. @@ -1839,6 +1835,8 @@ bool LLAppViewer::initThreads() LLWatchdog::getInstance()->init(watchdog_killer_callback); } + AICurlInterface::startCurlThread(); + LLImage::initClass(); LLVFSThread::initClass(enable_threads && false); diff --git a/indra/newview/llcurlrequest.cpp b/indra/newview/llcurlrequest.cpp new file mode 100644 index 000000000..9192de127 --- /dev/null +++ b/indra/newview/llcurlrequest.cpp @@ -0,0 +1,147 @@ +/** + * @file llcurlrequest.cpp + * @brief Implementation of Request. + * + * Copyright (c) 2012, Aleric Inglewood. + * Copyright (C) 2010, Linden Research, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + * + * 20/03/2012 + * Added copyright notice for Linden Lab for those parts that were + * copied or derived from llcurl.cpp. The code of those parts are + * already in their own llcurl.cpp, so they do not ever need to + * even look at this file; the reason I added the copyright notice + * is to make clear that I am not the author of 100% of this code + * and hence I cannot change the license of it. + */ + +#include "linden_common.h" + +#include "llsdserialize.h" +#include "llcurlrequest.h" +#include "statemachine/aicurleasyrequeststatemachine.h" + +//----------------------------------------------------------------------------- +// class Request +// + +namespace AICurlInterface { + +bool Request::get(std::string const& url, ResponderPtr responder) +{ + return getByteRange(url, headers_t(), 0, -1, responder); +} + +bool Request::getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder) +{ + DoutEntering(dc::curl, "Request::getByteRange(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + + AICurlResponderBuffer_wat(*buffered_easy_request->mCurlEasyRequest)->prepRequest(buffered_easy_request_w, headers, responder); + + buffered_easy_request_w->setopt(CURLOPT_HTTPGET, 1); + if (length > 0) + { + std::string range = llformat("Range: bytes=%d-%d", offset, offset + length - 1); + buffered_easy_request_w->addHeader(range.c_str()); + } + + buffered_easy_request_w->finalizeRequest(url); + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +bool Request::post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out) +{ + DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); + + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); + + buffer_w->getInput().write(data.data(), data.size()); + S32 bytes = buffer_w->getInput().str().length(); + buffered_easy_request_w->setPost(NULL, bytes); + buffered_easy_request_w->addHeader("Content-Type: application/octet-stream"); + buffered_easy_request_w->finalizeRequest(url); + + lldebugs << "POSTING: " << bytes << " bytes." << llendl; + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +bool Request::post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out) +{ + DoutEntering(dc::curl, "Request::post(" << url << ", ...)"); + + // This might throw AICurlNoEasyHandle. + AICurlEasyRequestStateMachine* buffered_easy_request = new AICurlEasyRequestStateMachine(true); + + { + AICurlEasyRequest_wat buffered_easy_request_w(*buffered_easy_request->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*buffered_easy_request->mCurlEasyRequest); + + buffer_w->prepRequest(buffered_easy_request_w, headers, responder); + + LLSDSerialize::toXML(data, buffer_w->getInput()); + S32 bytes = buffer_w->getInput().str().length(); + buffered_easy_request_w->setPost(NULL, bytes); + buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml"); + buffered_easy_request_w->finalizeRequest(url); + + lldebugs << "POSTING: " << bytes << " bytes." << llendl; + } + + buffered_easy_request->run(); + + return true; // We throw in case of problems. +} + +S32 Request::process(void) +{ + //FIXME: needs implementation + //DoutEntering(dc::warning, "Request::process()"); + return 0; +} + +} // namespace AICurlInterface +//================================================================================== + diff --git a/indra/newview/llcurlrequest.h b/indra/newview/llcurlrequest.h new file mode 100644 index 000000000..59f3da981 --- /dev/null +++ b/indra/newview/llcurlrequest.h @@ -0,0 +1,59 @@ +/** + * @file llcurlrequest.h + * @brief Declaration of class Request + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 17/03/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLREQUEST_H +#define AICURLREQUEST_H + +#include +#include +#include + +// Things defined in this namespace are called from elsewhere in the viewer code. +namespace AICurlInterface { + +// Forward declaration. +class Responder; +typedef boost::intrusive_ptr ResponderPtr; + +class Request { + public: + typedef std::vector headers_t; + + bool get(std::string const& url, ResponderPtr responder); + bool getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder); + bool post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out = 0); + bool post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out = 0); + + S32 process(void); +}; + +} // namespace AICurlInterface + +#endif diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 794e774fe..162414041 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -760,7 +760,7 @@ BOOL LLPanelRegionGeneralInfo::sendUpdate() body["allow_parcel_changes"] = childGetValue("allow_parcel_changes_check"); body["block_parcel_search"] = childGetValue("block_parcel_search_check"); - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } else { diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index caf2ce7da..cd44d3db9 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -35,7 +35,7 @@ #include "llappviewer.h" #include "llbufferstream.h" #include "llcallbacklist.h" -#include "llcurl.h" +#include "llcurlrequest.h" #include "lldatapacker.h" #include "llfasttimer.h" #if MESH_IMPORT @@ -465,7 +465,6 @@ public: LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo") { - mWaiting = false; mMutex = new LLMutex(); mHeaderMutex = new LLMutex(); mSignal = new LLCondition(); @@ -483,7 +482,7 @@ LLMeshRepoThread::~LLMeshRepoThread() void LLMeshRepoThread::run() { - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request; #if MESH_IMPORT LLCDResult res = LLConvexDecomposition::initThread(); if (res != LLCD_OK) @@ -492,13 +491,10 @@ void LLMeshRepoThread::run() } #endif //MESH_IMPORT + mSignal->lock(); while (!LLApp::isQuitting()) { - mWaiting = true; - mSignal->wait(); - mWaiting = false; - - if (!LLApp::isQuitting()) + // Left braces in order not to change the indentation. { static U32 count = 0; @@ -511,38 +507,53 @@ void LLMeshRepoThread::run() } // NOTE: throttling intentionally favors LOD requests over header requests - + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < (S32)sMaxConcurrentRequests) { - if (mMutex) { mMutex->lock(); LODRequest req = mLODReqQ.front(); mLODReqQ.pop(); LLMeshRepository::sLODProcessing--; mMutex->unlock(); - if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit + try { + fetchMeshLOD(req.mMeshParams, req.mLOD, count); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshLOD() failed: " << error.what() << llendl; mMutex->lock(); - mLODReqQ.push(req) ; + LLMeshRepository::sLODProcessing++; + mLODReqQ.push(req); mMutex->unlock(); + break; } } } while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < (S32)sMaxConcurrentRequests) { - if (mMutex) { mMutex->lock(); HeaderRequest req = mHeaderReqQ.front(); mHeaderReqQ.pop(); mMutex->unlock(); - if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit + bool success = false; + try + { + success = fetchMeshHeader(req.mMeshParams, count); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshHeader() failed: " << error.what() << llendl; + } + if (!success) { mMutex->lock(); mHeaderReqQ.push(req) ; mMutex->unlock(); + break; } } } @@ -552,7 +563,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshSkinInfo(mesh_id)) + bool success = false; + try + { + success = fetchMeshSkinInfo(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshSkinInfo(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -565,7 +585,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshDecomposition(mesh_id)) + bool success = false; + try + { + success = fetchMeshDecomposition(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshDecomposition(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -578,7 +607,16 @@ void LLMeshRepoThread::run() for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) { LLUUID mesh_id = *iter; - if (!fetchMeshPhysicsShape(mesh_id)) + bool success = false; + try + { + success = fetchMeshPhysicsShape(mesh_id); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "fetchMeshPhysicsShape(" << mesh_id << ") failed: " << error.what() << llendl; + } + if (!success) { incomplete.insert(mesh_id); } @@ -586,14 +624,11 @@ void LLMeshRepoThread::run() mPhysicsShapeRequests = incomplete; } - mCurlRequest->process(); } + + mSignal->wait(); } - - if (mSignal->isLocked()) - { //make sure to let go of the mutex associated with the given signal before shutting down - mSignal->unlock(); - } + mSignal->unlock(); #if MESH_IMPORT res = LLConvexDecomposition::quitThread(); @@ -681,21 +716,15 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { //protected by mMutex - - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, try again later. mHeaderMutex->unlock(); return false; } - bool ret = true ; U32 header_size = mMeshHeaderSize[mesh_id]; if (header_size > 0) @@ -743,12 +772,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshSkinInfoResponder(mesh_id, offset, size)); - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshSkinInfoResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } } @@ -758,26 +785,22 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } +//return false if failed to get header bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, try again later. mHeaderMutex->unlock(); return false; } U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; if (header_size > 0) { @@ -824,12 +847,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshDecompositionResponder(mesh_id, offset, size)); - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshDecompositionResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } } @@ -839,26 +860,22 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } +//return false if failed to get header bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) - { //we have no header info for this mesh, do nothing + { + // We have no header info for this mesh, retry later. mHeaderMutex->unlock(); return false; } U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; if (header_size > 0) { @@ -905,13 +922,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - ret = mCurlRequest->getByteRange(http_url, headers, offset, size, - new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); - - if(ret) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); + LLMeshRepository::sHTTPRequestCount++; } } else @@ -925,7 +939,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //early out was not hit, effectively fetched - return ret; + return true; } //return false if failed to get header @@ -944,14 +958,14 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c LLMeshRepository::sCacheBytesRead += bytes; file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) - { //did not do an HTTP request, return false + { + // Already have header, no need to retry. return true; } } } //either cache entry doesn't exist or is corrupt, request header from simulator - bool retval = true ; std::vector headers; headers.push_back("Accept: application/octet-stream"); @@ -961,29 +975,19 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits //within the first 4KB //NOTE -- this will break of headers ever exceed 4KB - retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); - if(retval) - { - LLMeshRepository::sHTTPRequestCount++; - } + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); + LLMeshRepository::sHTTPRequestCount++; count++; } - return retval; + return true; } -//return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) +void LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) { //protected by mMutex - if (!mHeaderMutex) - { - return false; - } - mHeaderMutex->lock(); - bool retval = true; - LLUUID mesh_id = mesh_params.getSculptID(); U32 header_size = mMeshHeaderSize[mesh_id]; @@ -1019,7 +1023,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, if (lodReceived(mesh_params, lod, buffer, size)) { delete[] buffer; - return true; + return; } } @@ -1033,13 +1037,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, std::string http_url = constructUrl(mesh_id); if (!http_url.empty()) { - retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + // This might throw AICurlNoEasyHandle. + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, new LLMeshLODResponder(mesh_params, lod, offset, size)); - - if(retval) - { - LLMeshRepository::sHTTPRequestCount++; - } + LLMeshRepository::sHTTPRequestCount++; count++; } else @@ -1056,8 +1057,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { mHeaderMutex->unlock(); } - - return retval; } bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) @@ -1102,7 +1101,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat LLMutexLock lock(mHeaderMutex); mMeshHeaderSize[mesh_id] = header_size; mMeshHeader[mesh_id] = header; - } + } //check for pending requests pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); @@ -1599,7 +1598,7 @@ void LLMeshUploadThread::generateHulls() void LLMeshUploadThread::doWholeModelUpload() { - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request(); if (mWholeModelUploadURL.empty()) { @@ -1614,6 +1613,7 @@ void LLMeshUploadThread::doWholeModelUpload() LLSD body = full_model_data["asset_resources"]; dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); LLCurlRequest::headers_t headers; + //FIXME: this might throw AICurlNoEasyHandle mCurlRequest->post(mWholeModelUploadURL, headers, body, new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); do @@ -1635,7 +1635,7 @@ void LLMeshUploadThread::requestWholeModelFee() { dump_num++; - mCurlRequest = new LLCurlRequest(); + mCurlRequest = new AICurlInterface::Request; generateHulls(); @@ -1645,6 +1645,7 @@ void LLMeshUploadThread::requestWholeModelFee() mPendingUploads++; LLCurlRequest::headers_t headers; + //FIXME: this might throw AICurlNoEasyHandle mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut); @@ -1665,11 +1666,6 @@ void LLMeshUploadThread::requestWholeModelFee() void LLMeshRepoThread::notifyLoadedMeshes() {//called via gMeshRepo.notifyLoadedMeshes(). mMutex already locked - if (!mMutex) - { - return; - } - while (!mLoadedQ.empty()) { mMutex->lock(); @@ -2373,17 +2369,17 @@ void LLMeshRepository::notifyLoadedMeshes() mInventoryQ.pop(); } - } - #endif //MESH_IMPORT //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - if (!mThread->mWaiting) - { //curl thread is churning, wait for it to go idle + if (!mThread->mSignal->tryLock()) + { + // Curl thread is churning, wait for it to go idle. return; } + mThread->mSignal->unlock(); static std::string region_name("never name a region this"); @@ -3329,6 +3325,7 @@ void LLPhysicsDecomp::run() mStageID[stages[i].mName] = i; } + mSignal->lock(); while (!mQuitting) { mSignal->wait(); @@ -3357,14 +3354,10 @@ void LLPhysicsDecomp::run() } } } + mSignal->unlock(); decomp->quitThread(); - if (mSignal->isLocked()) - { //let go of mSignal's associated mutex - mSignal->unlock(); - } - mDone = true; #endif //MESH_IMPORT } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index c106f810f..5d225cfe4 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -32,6 +32,7 @@ #include "lluuid.h" #include "llviewertexture.h" #include "llvolume.h" +#include "llcurlrequest.h" #if MESH_IMPORT #define LLCONVEXDECOMPINTER_STATIC 1 @@ -67,7 +68,6 @@ struct LLCDHull class LLVOVolume; class LLMeshResponder; -class LLCurlRequest; class LLMutex; class LLCondition; class LLVFS; @@ -246,13 +246,11 @@ public: static S32 sActiveLODRequests; static U32 sMaxConcurrentRequests; - LLCurlRequest* mCurlRequest; + AICurlInterface::Request* mCurlRequest; LLMutex* mMutex; LLMutex* mHeaderMutex; LLCondition* mSignal; - bool mWaiting; - //map of known mesh headers typedef std::map mesh_header_map; mesh_header_map mMeshHeader; @@ -351,7 +349,7 @@ public: void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count); - bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); + void fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 5fe634eed..5421cd886 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -999,7 +999,7 @@ void LLPanelClassified::sendClassifiedClickMessage(const std::string& type) std::string url = gAgent.getRegion()->getCapability("SearchStatTracking"); llinfos << "LLPanelClassified::sendClassifiedClickMessage via capability" << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a1fad9902..af0b25add 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3589,6 +3589,7 @@ std::string LLStartUp::startupStateToString(EStartupState state) #define RTNENUM(E) case E: return #E switch(state){ RTNENUM( STATE_FIRST ); + RTNENUM( STATE_BROWSER_INIT ); RTNENUM( STATE_LOGIN_SHOW ); RTNENUM( STATE_LOGIN_WAIT ); RTNENUM( STATE_LOGIN_CLEANUP ); @@ -3596,10 +3597,13 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_UPDATE_CHECK ); RTNENUM( STATE_LOGIN_AUTH_INIT ); RTNENUM( STATE_LOGIN_AUTHENTICATE ); + RTNENUM( STATE_WAIT_LEGACY_LOGIN ); + RTNENUM( STATE_XMLRPC_LEGACY_LOGIN ); RTNENUM( STATE_LOGIN_NO_DATA_YET ); RTNENUM( STATE_LOGIN_DOWNLOADING ); RTNENUM( STATE_LOGIN_PROCESS_RESPONSE ); RTNENUM( STATE_WORLD_INIT ); + RTNENUM( STATE_MULTIMEDIA_INIT ); RTNENUM( STATE_FONT_INIT ); RTNENUM( STATE_SEED_GRANTED_WAIT ); RTNENUM( STATE_SEED_CAP_GRANTED ); @@ -3612,10 +3616,10 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_WEARABLES_WAIT ); RTNENUM( STATE_CLEANUP ); RTNENUM( STATE_STARTED ); - default: - return llformat("(state #%d)", state); } #undef RTNENUM + // Never reached. + return llformat("(state #%d)", state); } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 72132496e..a638e39fd 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1319,8 +1319,15 @@ bool LLTextureFetchWorker::doWork(S32 param) // 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)); + try + { + res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, + new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << error.what() << llendl; + } } if (!res) { @@ -2435,7 +2442,7 @@ void LLTextureFetch::shutDownImageDecodeThread() void LLTextureFetch::startThread() { // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlRequest(); + mCurlGetRequest = new AICurlInterface::Request; } // WORKER THREAD diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index ee3cd2c75..03073d906 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -37,7 +37,7 @@ #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" +#include "llcurlrequest.h" #include "lltextureinfo.h" #include "llapr.h" @@ -107,7 +107,7 @@ public: LLViewerAssetStats * main_stats); void commandDataBreak(); - LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } + AICurlInterface::Request& getCurlRequest() { return *mCurlGetRequest; } bool isQAMode() const { return mQAMode; } @@ -178,7 +178,7 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlRequest* mCurlGetRequest; + AICurlInterface::Request* mCurlGetRequest; // Map of all requests by UUID typedef std::map map_t; diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 1493f4abb..fb78b29fa 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -462,7 +462,7 @@ void LLViewerParcelMedia::sendMediaNavigateMessage(const std::string& url) body["agent-id"] = gAgent.getID(); body["local-id"] = LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID(); body["url"] = url; - LLHTTPClient::post(region_url, body, new LLHTTPClient::Responder); + LLHTTPClient::post(region_url, body, new LLHTTPClient::ResponderIgnore); } else { diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 64f20ff50..529d0e034 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1310,7 +1310,7 @@ void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_ag parcel->packMessage(body); llinfos << "Sending parcel properties update via capability to: " << url << llendl; - LLHTTPClient::post(url, body, new LLHTTPClient::Responder()); + LLHTTPClient::post(url, body, new LLHTTPClient::ResponderIgnore); } else { diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index af5e977b2..5abe2df2e 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -43,6 +43,11 @@ #include "llappviewer.h" #include "hippogridmanager.h" +#include "statemachine/aicurleasyrequeststatemachine.h" + +#ifdef CWDEBUG +#include +#endif LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const { @@ -154,7 +159,7 @@ class LLXMLRPCTransaction::Impl public: typedef LLXMLRPCTransaction::Status Status; - LLCurlEasyRequest* mCurlRequest; + AICurlEasyRequestStateMachine* mCurlEasyRequestStateMachinePtr; Status mStatus; CURLcode mCurlCode; @@ -176,7 +181,8 @@ public: const std::string& method, LLXMLRPCValue params, bool useGzip); ~Impl(); - bool process(); + bool is_finished(void) const; + void curlEasyRequestCallback(bool success); void setStatus(Status code, const std::string& message = "", const std::string& uri = ""); @@ -191,7 +197,7 @@ private: LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip) - : mCurlRequest(0), + : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), mRequestText(0), @@ -203,7 +209,7 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, LLXMLRPCTransaction::Impl::Impl(const std::string& uri, const std::string& method, LLXMLRPCValue params, bool useGzip) - : mCurlRequest(0), + : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), mRequestText(0), @@ -222,60 +228,66 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { - if (!mCurlRequest) { - mCurlRequest = new LLCurlEasyRequest(); - } + try + { + mCurlEasyRequestStateMachinePtr = new AICurlEasyRequestStateMachine(false); + } + catch(AICurlNoEasyHandle const& error) + { + llwarns << "Failed to initialize LLXMLRPCTransaction: " << error.what() << llendl; + setStatus(StatusOtherError, "No curl easy handle"); + return; + } + AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequestStateMachinePtr->mCurlEasyRequest); - if(!mCurlRequest->isValid()) + curlEasyRequest_w->setWriteCallback(&curlDownloadCallback, (void*)this); + BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); + // Be a little impatient about establishing connections. + curlEasyRequest_w->setopt(CURLOPT_CONNECTTIMEOUT, 40L); + + /* Setting the DNS cache timeout to -1 disables it completely. + This might help with bug #503 */ + curlEasyRequest_w->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); + + curlEasyRequest_w->addHeader("Content-Type: text/xml"); + + if (useGzip) + { + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); + } + + mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); + if (mRequestText) + { + Dout(dc::curl, "Writing " << mRequestTextSize << " bytes: \"" << libcwd::buf2str(mRequestText, mRequestTextSize) << "\".");; + curlEasyRequest_w->setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)mRequestTextSize); + curlEasyRequest_w->setoptString(CURLOPT_COPYPOSTFIELDS, mRequestText); + } + else + { + setStatus(StatusOtherError); + } + + curlEasyRequest_w->finalizeRequest(mURI); + } + if (mStatus == LLXMLRPCTransaction::StatusNotStarted) // It could be LLXMLRPCTransaction::StatusOtherError. { - llwarns << "mCurlRequest is invalid." << llendl ; - - delete mCurlRequest ; - mCurlRequest = NULL ; - return ; + mCurlEasyRequestStateMachinePtr->run(boost::bind(&LLXMLRPCTransaction::Impl::curlEasyRequestCallback, this, _1)); + setStatus(LLXMLRPCTransaction::StatusStarted); } - - - LLProxy::getInstance()->applyProxySettings(mCurlRequest); - -// mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging - mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); - mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); - // Be a little impatient about establishing connections. - mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); - - /* Setting the DNS cache timeout to -1 disables it completely. - This might help with bug #503 */ - mCurlRequest->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); - - mCurlRequest->slist_append("Content-Type: text/xml"); - - if (useGzip) - { - mCurlRequest->setoptString(CURLOPT_ENCODING, ""); - } - - mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); - if (mRequestText) - { - mCurlRequest->setoptString(CURLOPT_POSTFIELDS, mRequestText); - mCurlRequest->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); - } - else - { - setStatus(StatusOtherError); - } - - mCurlRequest->sendRequest(mURI); } - LLXMLRPCTransaction::Impl::~Impl() { + if (mCurlEasyRequestStateMachinePtr && mCurlEasyRequestStateMachinePtr->running()) + { + llwarns << "Calling LLXMLRPCTransaction::Impl::~Impl while mCurlEasyRequestStateMachinePtr is still running" << llendl; + mCurlEasyRequestStateMachinePtr->abort(); + } + if (mResponse) { XMLRPC_RequestFree(mResponse, 1); @@ -285,119 +297,76 @@ LLXMLRPCTransaction::Impl::~Impl() { XMLRPC_Free(mRequestText); } - - delete mCurlRequest; - mCurlRequest = NULL ; } -bool LLXMLRPCTransaction::Impl::process() +bool LLXMLRPCTransaction::Impl::is_finished(void) const { - if(!mCurlRequest || !mCurlRequest->isValid()) - { - llwarns << "transaction failed." << llendl ; + // Nothing to process anymore. Just wait till the statemachine finished. + return mStatus != LLXMLRPCTransaction::StatusNotStarted && + mStatus != LLXMLRPCTransaction::StatusStarted && + mStatus != LLXMLRPCTransaction::StatusDownloading; +} - delete mCurlRequest ; - mCurlRequest = NULL ; - return true ; //failed, quit. +void LLXMLRPCTransaction::Impl::curlEasyRequestCallback(bool success) +{ + llassert(mStatus == LLXMLRPCTransaction::StatusStarted || mStatus == LLXMLRPCTransaction::StatusDownloading); + + if (!success) + { + setStatus(LLXMLRPCTransaction::StatusOtherError, "Statemachine failed"); + return; } - switch(mStatus) + AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequestStateMachinePtr->mCurlEasyRequest); + CURLcode result; + curlEasyRequest_w->getResult(&result, &mTransferInfo); + + if (result != CURLE_OK) { - case LLXMLRPCTransaction::StatusComplete: - case LLXMLRPCTransaction::StatusCURLError: - case LLXMLRPCTransaction::StatusXMLRPCError: - case LLXMLRPCTransaction::StatusOtherError: - { - return true; - } - - case LLXMLRPCTransaction::StatusNotStarted: - { - setStatus(LLXMLRPCTransaction::StatusStarted); - break; - } - - default: - { - // continue onward - } + setCurlStatus(result); + llwarns << "LLXMLRPCTransaction CURL error " + << mCurlCode << ": " << curlEasyRequest_w->getErrorString() << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; + + return; } - //const F32 MAX_PROCESSING_TIME = 0.05f; - //LLTimer timer; + setStatus(LLXMLRPCTransaction::StatusComplete); - mCurlRequest->wait(); + mResponse = XMLRPC_REQUEST_FromXML( + mResponseText.data(), mResponseText.size(), NULL); - /*while (mCurlRequest->perform() > 0) + bool hasError = false; + bool hasFault = false; + int faultCode = 0; + std::string faultString; + + LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse)); + if (error.isValid()) { - if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME) - { - return false; - } - }*/ - - while(1) - { - CURLcode result; - bool newmsg = mCurlRequest->getResult(&result, &mTransferInfo); - if (newmsg) - { - if (result != CURLE_OK) - { - setCurlStatus(result); - llwarns << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; - - return true; - } - - setStatus(LLXMLRPCTransaction::StatusComplete); - - mResponse = XMLRPC_REQUEST_FromXML( - mResponseText.data(), mResponseText.size(), NULL); - - bool hasError = false; - bool hasFault = false; - int faultCode = 0; - std::string faultString; - - LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse)); - if (error.isValid()) - { - hasError = true; - faultCode = error["faultCode"].asInt(); - faultString = error["faultString"].asString(); - } - else if (XMLRPC_ResponseIsFault(mResponse)) - { - hasFault = true; - faultCode = XMLRPC_GetResponseFaultCode(mResponse); - faultString = XMLRPC_GetResponseFaultString(mResponse); - } - - if (hasError || hasFault) - { - setStatus(LLXMLRPCTransaction::StatusXMLRPCError); - - llwarns << "LLXMLRPCTransaction XMLRPC " - << (hasError ? "error " : "fault ") - << faultCode << ": " - << faultString << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; - } - - return true; - } - else - { - break; // done - } + hasError = true; + faultCode = error["faultCode"].asInt(); + faultString = error["faultString"].asString(); + } + else if (XMLRPC_ResponseIsFault(mResponse)) + { + hasFault = true; + faultCode = XMLRPC_GetResponseFaultCode(mResponse); + faultString = XMLRPC_GetResponseFaultString(mResponse); + } + + if (hasError || hasFault) + { + setStatus(LLXMLRPCTransaction::StatusXMLRPCError); + + llwarns << "LLXMLRPCTransaction XMLRPC " + << (hasError ? "error " : "fault ") + << faultCode << ": " + << faultString << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; } - - return false; } void LLXMLRPCTransaction::Impl::setStatus(Status status, @@ -489,6 +458,8 @@ void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code) size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( char* data, size_t size, size_t nmemb, void* user_data) { + DoutEntering(dc::curl, "LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, size * nmemb) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); + Impl& impl(*(Impl*)user_data); size_t n = size * nmemb; @@ -523,7 +494,7 @@ LLXMLRPCTransaction::~LLXMLRPCTransaction() bool LLXMLRPCTransaction::process() { - return impl.process(); + return impl.is_finished(); } LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode) diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index 528451fcb..42c236265 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -111,7 +111,7 @@ public: } Status; bool process(); - // run the request a little, returns true when done + // Returns true when done. Status status(int* curlCode); // return status, and extended CURL code, if code isn't null @@ -127,7 +127,7 @@ public: // retains ownership of the result object, don't free it F64 transferRate(); - // only valid if StsatusComplete, otherwise 0.0 + // only valid if StatusComplete, otherwise 0.0 private: class Impl; diff --git a/indra/newview/statemachine/CMakeLists.txt b/indra/newview/statemachine/CMakeLists.txt index d36caca38..08ecccecf 100644 --- a/indra/newview/statemachine/CMakeLists.txt +++ b/indra/newview/statemachine/CMakeLists.txt @@ -5,7 +5,7 @@ project(statemachine) include(00-Common) include(LLCommon) include(LLPlugin) -include(LLMessage) # This is needed by LLPlugin. +include(LLMessage) include(LLMath) include(LLVFS) include(LLXML) @@ -38,6 +38,7 @@ include_directories( set(statemachine_SOURCE_FILES aistatemachine.cpp + aicurleasyrequeststatemachine.cpp aifilepicker.cpp aifetchinventoryfolder.cpp aievent.cpp @@ -47,6 +48,7 @@ set(statemachine_SOURCE_FILES set(statemachine_HEADER_FILES CMakeLists.txt aistatemachine.h + aicurleasyrequeststatemachine.h aifilepicker.h aidirpicker.h aifetchinventoryfolder.h diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp new file mode 100644 index 000000000..a308be093 --- /dev/null +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp @@ -0,0 +1,152 @@ +/** + * @file aicurleasyrequeststatemachine.cpp + * @brief Implementation of AICurlEasyRequestStateMachine + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 06/05/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "linden_common.h" +#include "aicurleasyrequeststatemachine.h" + +enum curleasyrequeststatemachine_state_type { + AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state, + AICurlEasyRequestStateMachine_waitAdded, + AICurlEasyRequestStateMachine_waitFinished, + AICurlEasyRequestStateMachine_finished +}; + +char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) const +{ + switch(run_state) + { + AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitFinished); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished); + } + return "UNKNOWN STATE"; +} + +void AICurlEasyRequestStateMachine::initialize_impl(void) +{ + { + AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequest); + llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest(url) before calling run(). + curlEasyRequest_w->send_events_to(this); + } + set_state(AICurlEasyRequestStateMachine_addRequest); +} + +// CURL-THREAD +void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&) +{ + set_state(AICurlEasyRequestStateMachine_waitFinished); +} + +// CURL-THREAD +void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&) +{ +} + +// CURL-THREAD +void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_wat&) +{ + set_state(AICurlEasyRequestStateMachine_finished); +} + +void AICurlEasyRequestStateMachine::multiplex_impl(void) +{ + switch (mRunState) + { + case AICurlEasyRequestStateMachine_addRequest: + { + mCurlEasyRequest.addRequest(); + set_state(AICurlEasyRequestStateMachine_waitAdded); + } + case AICurlEasyRequestStateMachine_waitAdded: + { + idle(); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. + break; + } + case AICurlEasyRequestStateMachine_waitFinished: + { + idle(); // Wait till AICurlEasyRequestStateMachine::finished() is called. + break; + } + case AICurlEasyRequestStateMachine_finished: + { + if (mBuffered) + { + AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest); + AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest); + buffered_easy_request_w->processOutput(easy_request_w); + } + finish(); + break; + } + } +} + +void AICurlEasyRequestStateMachine::abort_impl(void) +{ + Dout(dc::curl, "AICurlEasyRequestStateMachine::abort_impl called for = " << (void*)mCurlEasyRequest.get()); + // We must first revoke the events, or the curl thread might change mRunState still. + { + AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest); + curl_easy_request_w->send_events_to(NULL); + curl_easy_request_w->revokeCallbacks(); + } + if (mRunState >= AICurlEasyRequestStateMachine_waitAdded && mRunState < AICurlEasyRequestStateMachine_finished) + { + // Revert call to addRequest(). + // Note that it's safe to call this even if the curl thread already removed it, or will removes it + // after we called this, before processing the remove command; only the curl thread calls + // MultiHandle::remove_easy_request, which is a no-op when called twice for the same easy request. + mCurlEasyRequest.removeRequest(); + } +} + +void AICurlEasyRequestStateMachine::finish_impl(void) +{ + Dout(dc::curl, "AICurlEasyRequestStateMachine::finish_impl called for = " << (void*)mCurlEasyRequest.get()); + if (!aborted()) + { + AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest); + curl_easy_request_w->send_events_to(NULL); + curl_easy_request_w->revokeCallbacks(); + } +} + +AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) : mBuffered(buffered), mCurlEasyRequest(buffered) +{ + Dout(dc::statemachine, "Calling AICurlEasyRequestStateMachine(" << (buffered ? "true" : "false") << ") [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); +} + +AICurlEasyRequestStateMachine::~AICurlEasyRequestStateMachine() +{ + Dout(dc::statemachine, "Calling ~AICurlEasyRequestStateMachine() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); +} + diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.h b/indra/newview/statemachine/aicurleasyrequeststatemachine.h new file mode 100644 index 000000000..41c3c7256 --- /dev/null +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.h @@ -0,0 +1,96 @@ +/** + * @file aicurleasyrequest.h + * @brief Perform a curl easy request. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 06/05/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AICURLEASYREQUEST_H +#define AICURLEASYREQUEST_H + +#include "aistatemachine.h" +#include "aicurl.h" + +// A curl easy request state machine. +// +// Before calling cersm.run() initialize the object (cersm) as follows: +// +// AICurlEasyRequest_wat cersm_w(cersm); +// cersm_w->setopt(...); // etc, see the interface of AICurlPrivate::CurlEasyRequest and it's base class AICurlPrivate::CurlEasyHandle. +// +// When the state machine finishes, call aborted() to check +// whether or not the statemachine succeeded in fetching +// the URL or not. +// +// Objects of this type can be reused multiple times, see +// also the documentation of AIStateMachine. +// +// Construction of a AICurlEasyRequestStateMachine might throw AICurlNoEasyHandle. +class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHandleEvents { + public: + AICurlEasyRequestStateMachine(bool buffered); + + // Transparent access. + AICurlEasyRequest mCurlEasyRequest; + + private: + bool mBuffered; // Argument used for construction of mCurlEasyRequest. + + protected: + // AICurlEasyRequest Events. + + // Called when this curl easy handle was added to a multi handle. + /*virtual*/ void added_to_multi_handle(AICurlEasyRequest_wat&); + + // Called when this curl easy handle finished processing (right before it is removed from the multi handle). + /*virtual*/ void finished(AICurlEasyRequest_wat&); + + // Called after this curl easy handle was removed from a multi handle. + /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat&); + + protected: + // AIStateMachine implementations. + + // Call finish() (or abort()), not delete. + /*virtual*/ ~AICurlEasyRequestStateMachine(); + + // Handle initializing the object. + /*virtual*/ void initialize_impl(void); + + // Handle mRunState. + /*virtual*/ void multiplex_impl(void); + + // Handle aborting from current bs_run state. + /*virtual*/ void abort_impl(void); + + // Handle cleaning up from initialization (or post abort) state. + /*virtual*/ void finish_impl(void); + + // Implemenation of state_str for run states. + /*virtual*/ char const* state_str_impl(state_type run_state) const; +}; + +#endif diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index f20fcf048..16eb28cd4 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -237,7 +237,7 @@ class AIStateMachine { protected: //! The user should call 'kill()', not delete a AIStateMachine (derived) directly. - virtual ~AIStateMachine() { llassert(mState == bs_killed && mActive == as_idle); } + virtual ~AIStateMachine() { llassert((mState == bs_killed && mActive == as_idle) || mState == bs_initialize); } public: //! Halt the state machine until cont() is called. From 1f56645b69b853024e77234bd3d5d13785280970 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 28 Jun 2012 05:56:21 +0200 Subject: [PATCH 002/123] Always set proxy settings for every HTTP curl connection. Move applyProxySettings to CurlEasyRequest and call it from applyDefaultOptions. Use AIThreadSafe for LLProxy for a more robust threadsafeness. (This forces correct locking, checks that the unshared vars are indeed unshared and made it easy to use read/write locking, which might be important in this case (we do a lot of read-only accesses to it). --- indra/llmessage/aicurl.cpp | 35 +++++- indra/llmessage/aicurlprivate.h | 4 + indra/llmessage/llproxy.cpp | 181 +++++++++----------------------- indra/llmessage/llproxy.h | 152 ++++++++++++++++----------- 4 files changed, 179 insertions(+), 193 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6278c91c2..3ab86f56f 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -805,6 +805,39 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t } #endif +void CurlEasyRequest::applyProxySettings(void) +{ + LLProxy& proxy = *LLProxy::getInstance(); + + // Do a faster unlocked check to see if we are supposed to proxy. + if (proxy.HTTPProxyEnabled()) + { + // We think we should proxy, read lock the shared proxy members. + LLProxy::Shared_crat proxy_r(proxy.shared_lockobj()); + + // Now test again to verify that the proxy wasn't disabled between the first check and the lock. + if (proxy.HTTPProxyEnabled()) + { + setopt(CURLOPT_PROXY, proxy.getHTTPProxy(proxy_r).getIPString().c_str()); + setopt(CURLOPT_PROXYPORT, proxy.getHTTPProxy(proxy_r).getPort()); + + if (proxy.getHTTPProxyType(proxy_r) == LLPROXY_SOCKS) + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + if (proxy.getSelectedAuthMethod(proxy_r) == METHOD_PASSWORD) + { + std::string auth_string = proxy.getSocksUser(proxy_r) + ":" + proxy.getSocksPwd(proxy_r); + setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); + } + } + else + { + setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + } + } +} + void CurlEasyRequest::applyDefaultOptions(void) { CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); @@ -822,7 +855,7 @@ void CurlEasyRequest::applyDefaultOptions(void) // The old code did this for the 'buffered' version, but I think it's nonsense. //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); // Set the CURL options for either Socks or HTTP proxy. - LLProxy::getInstance()->applyProxySettings(this); + applyProxySettings(); Debug( if (dc::curl.is_on()) { diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 9a7f50180..fcf51e927 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -176,6 +176,10 @@ class CurlEasyRequest : public CurlEasyHandle { // Reset everything to the state it was in when this object was just created. void resetState(void); + private: + // Called from applyDefaultOptions. + void applyProxySettings(void); + public: // Set default options that we want applied to all curl easy handles. void applyDefaultOptions(void); diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 3db851001..a82ae3cc7 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -47,23 +47,22 @@ static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataou static LLSocket::ptr_t tcp_open_channel(LLHost host); // Open a TCP channel to a given host static void tcp_close_channel(LLSocket::ptr_t* handle_ptr); // Close an open TCP channel -LLProxy::LLProxy(): - mHTTPProxyEnabled(false), - mProxyMutex(), - mUDPProxy(), - mTCPProxy(), - mHTTPProxy(), +ProxyShared::ProxyShared(void): mProxyType(LLPROXY_SOCKS), - mAuthMethodSelected(METHOD_NOAUTH), - mSocksUsername(), - mSocksPassword() + mAuthMethodSelected(METHOD_NOAUTH) +{ +} + +LLProxy::LLProxy(): + mHTTPProxyEnabled(false) { } LLProxy::~LLProxy() { stopSOCKSProxy(); - disableHTTPProxy(); + Shared_wat shared_w(mShared); + disableHTTPProxy(shared_w); } /** @@ -78,15 +77,18 @@ S32 LLProxy::proxyHandshake(LLHost proxy) { S32 result; + Unshared_rat unshared_r(mUnshared); + Shared_rat shared_r(mShared); + /* SOCKS 5 Auth request */ socks_auth_request_t socks_auth_request; socks_auth_response_t socks_auth_response; socks_auth_request.version = SOCKS_VERSION; // SOCKS version 5 socks_auth_request.num_methods = 1; // Sending 1 method. - socks_auth_request.methods = getSelectedAuthMethod(); // Send only the selected method. + socks_auth_request.methods = getSelectedAuthMethod(shared_r); // Send only the selected method. - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&socks_auth_request)), sizeof(socks_auth_request), static_cast(static_cast(&socks_auth_response)), @@ -109,8 +111,8 @@ S32 LLProxy::proxyHandshake(LLHost proxy) if (socks_auth_response.method == METHOD_PASSWORD) { // The server has requested a username/password combination - std::string socks_username(getSocksUser()); - std::string socks_password(getSocksPwd()); + std::string socks_username(getSocksUser(shared_r)); + std::string socks_password(getSocksPwd(shared_r)); U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; @@ -121,7 +123,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) authmethod_password_reply_t password_reply; - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, password_auth, request_size, static_cast(static_cast(&password_reply)), @@ -157,7 +159,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) // "If the client is not in possession of the information at the time of the UDP ASSOCIATE, // the client MUST use a port number and address of all zeros. RFC 1928" - result = tcp_blocking_handshake(mProxyControlChannel, + result = tcp_blocking_handshake(unshared_r->mProxyControlChannel, static_cast(static_cast(&connect_request)), sizeof(connect_request), static_cast(static_cast(&connect_reply)), @@ -176,10 +178,14 @@ S32 LLProxy::proxyHandshake(LLHost proxy) return SOCKS_UDP_FWD_NOT_GRANTED; } - mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order - mUDPProxy.setAddress(proxy.getAddress()); + { + // Write acccess type and read access type are really the same, so unshared_w must be simply a reference. + Unshared_wat& unshared_w = unshared_r; + unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order + unshared_w->mUDPProxy.setAddress(proxy.getAddress()); + } // The connection was successful. We now have the UDP port to send requests that need forwarding to. - LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << mUDPProxy << LL_ENDL; + LL_INFOS("Proxy") << "SOCKS 5 UDP proxy connected on " << unshared_r->mUDPProxy << LL_ENDL; return SOCKS_OK; } @@ -197,9 +203,11 @@ S32 LLProxy::proxyHandshake(LLHost proxy) */ S32 LLProxy::startSOCKSProxy(LLHost host) { + Unshared_wat unshared_w(mUnshared); + if (host.isOk()) { - mTCPProxy = host; + unshared_w->mTCPProxy = host; } else { @@ -209,13 +217,13 @@ S32 LLProxy::startSOCKSProxy(LLHost host) // Close any running SOCKS connection. stopSOCKSProxy(); - mProxyControlChannel = tcp_open_channel(mTCPProxy); - if (!mProxyControlChannel) + unshared_w->mProxyControlChannel = tcp_open_channel(unshared_w->mTCPProxy); + if (!unshared_w->mProxyControlChannel) { return SOCKS_HOST_CONNECT_FAILED; } - S32 status = proxyHandshake(mTCPProxy); + S32 status = proxyHandshake(unshared_w->mTCPProxy); if (status != SOCKS_OK) { @@ -246,14 +254,16 @@ void LLProxy::stopSOCKSProxy() // then we must shut down any HTTP proxy operations. But it is allowable if web // proxy is being used to continue proxying HTTP. - if (LLPROXY_SOCKS == getHTTPProxyType()) + Shared_rat shared_r(mShared); + if (LLPROXY_SOCKS == getHTTPProxyType(shared_r)) { - disableHTTPProxy(); + Shared_wat shared_w(shared_r); + disableHTTPProxy(shared_w); } - - if (mProxyControlChannel) + Unshared_wat unshared_w(mUnshared); + if (unshared_w->mProxyControlChannel) { - tcp_close_channel(&mProxyControlChannel); + tcp_close_channel(&unshared_w->mProxyControlChannel); } } @@ -262,9 +272,7 @@ void LLProxy::stopSOCKSProxy() */ void LLProxy::setAuthNone() { - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_NOAUTH; + Shared_wat(mShared)->mAuthMethodSelected = METHOD_NOAUTH; } /** @@ -288,11 +296,10 @@ bool LLProxy::setAuthPassword(const std::string &username, const std::string &pa return false; } - LLMutexLock lock(&mProxyMutex); - - mAuthMethodSelected = METHOD_PASSWORD; - mSocksUsername = username; - mSocksPassword = password; + Shared_wat shared_w(mShared); + shared_w->mAuthMethodSelected = METHOD_PASSWORD; + shared_w->mSocksUsername = username; + shared_w->mSocksPassword = password; return true; } @@ -314,12 +321,10 @@ bool LLProxy::enableHTTPProxy(LLHost httpHost, LLHttpProxyType type) return false; } - LLMutexLock lock(&mProxyMutex); - - mHTTPProxy = httpHost; - mProxyType = type; - + Shared_wat shared_w(mShared); mHTTPProxyEnabled = true; + shared_w->mHTTPProxy = httpHost; + shared_w->mProxyType = type; return true; } @@ -335,9 +340,8 @@ bool LLProxy::enableHTTPProxy() { bool ok; - LLMutexLock lock(&mProxyMutex); - - ok = (mHTTPProxy.isOk()); + Shared_rat shared_r(mShared); + ok = (shared_r->mHTTPProxy.isOk()); if (ok) { mHTTPProxyEnabled = true; @@ -346,54 +350,6 @@ bool LLProxy::enableHTTPProxy() return ok; } -/** - * @brief Disable the HTTP proxy. - */ -void LLProxy::disableHTTPProxy() -{ - LLMutexLock lock(&mProxyMutex); - - mHTTPProxyEnabled = false; -} - -/** - * @brief Get the currently selected HTTP proxy type - */ -LLHttpProxyType LLProxy::getHTTPProxyType() const -{ - LLMutexLock lock(&mProxyMutex); - return mProxyType; -} - -/** - * @brief Get the SOCKS 5 password. - */ -std::string LLProxy::getSocksPwd() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksPassword; -} - -/** - * @brief Get the SOCKS 5 username. - */ -std::string LLProxy::getSocksUser() const -{ - LLMutexLock lock(&mProxyMutex); - return mSocksUsername; -} - -/** - * @brief Get the currently selected SOCKS 5 authentication method. - * - * @return Returns either none or password. - */ -LLSocks5AuthType LLProxy::getSelectedAuthMethod() const -{ - LLMutexLock lock(&mProxyMutex); - return mAuthMethodSelected; -} - /** * @brief Stop the LLProxy and make certain that any APR pools and classes are deleted before terminating APR. * @@ -406,47 +362,6 @@ void LLProxy::cleanupClass() deleteSingleton(); } -/** - * @brief Apply proxy settings to a CuRL request if an HTTP proxy is enabled. - * - * This method has been designed to be safe to call from - * any thread in the viewer. This allows requests in the - * texture fetch thread to be aware of the proxy settings. - * When the HTTP proxy is enabled, the proxy mutex will - * be locked every time this method is called. - * - * @param curlEasyRequest_w An already locked curl easy handle, before it has been performed. - */ -void LLProxy::applyProxySettings(AICurlEasyRequest_wat const& curlEasyRequest_w) -{ - // Do a faster unlocked check to see if we are supposed to proxy. - if (mHTTPProxyEnabled) - { - // We think we should proxy, lock the proxy mutex. - LLMutexLock lock(&mProxyMutex); - // Now test again to verify that the proxy wasn't disabled between the first check and the lock. - if (mHTTPProxyEnabled) - { - curlEasyRequest_w->setopt(CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()); - curlEasyRequest_w->setopt(CURLOPT_PROXYPORT, mHTTPProxy.getPort()); - - if (mProxyType == LLPROXY_SOCKS) - { - curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - if (mAuthMethodSelected == METHOD_PASSWORD) - { - std::string auth_string = mSocksUsername + ":" + mSocksPassword; - curlEasyRequest_w->setopt(CURLOPT_PROXYUSERPWD, auth_string.c_str()); - } - } - else - { - curlEasyRequest_w->setopt(CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - } - } - } -} - /** * @brief Send one TCP packet and receive one in return. * diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index 94f031fc3..c0d6ab183 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -33,6 +33,7 @@ #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include "aithreadsafe.h" #include // SOCKS error codes returned from the StartProxy method @@ -211,28 +212,87 @@ enum LLSocks5AuthType * To ensure thread safety, all LLProxy members that relate to the HTTP * proxy require the LLProxyMutex to be locked before accessing. */ + +struct ProxyUnshared +{ + /*########################################################################################### + MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. + ###########################################################################################*/ + + // UDP proxy address and port + LLHost mUDPProxy; + + // TCP proxy control channel address and port + LLHost mTCPProxy; + + // socket handle to proxy TCP control channel + LLSocket::ptr_t mProxyControlChannel; + + /*########################################################################################### + END OF UNSHARED MEMBERS + ###########################################################################################*/ +}; + +struct ProxyShared +{ + ProxyShared(void); + + /*########################################################################################### + MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. + ###########################################################################################*/ + + // HTTP proxy address and port + LLHost mHTTPProxy; + + // Currently selected HTTP proxy type. Can be web or socks. + LLHttpProxyType mProxyType; + + // SOCKS 5 selected authentication method. + LLSocks5AuthType mAuthMethodSelected; + + // SOCKS 5 username + std::string mSocksUsername; + // SOCKS 5 password + std::string mSocksPassword; + + /*########################################################################################### + END OF SHARED MEMBERS + ###########################################################################################*/ +}; + class LLProxy: public LLSingleton { LOG_CLASS(LLProxy); + public: + typedef AISTAccessConst Unshared_crat; // Constant Read Access Type for Unshared (cannot be converted to write access). + typedef AISTAccess Unshared_rat; // Read Access Type for Unshared (same as write access type, since we don't lock at all). + typedef AISTAccess Unshared_wat; // Write Access Type, for Unshared. + typedef AIReadAccessConst Shared_crat; // Constant Read Access Type for Shared (cannot be converted to write access). + typedef AIReadAccess Shared_rat; // Read Access Type for Shared. + typedef AIWriteAccess Shared_wat; // Write Access Type for Shared. + /*########################################################################################### - METHODS THAT DO NOT LOCK mProxyMutex! + Public methods that only access variables not shared between threads. ###########################################################################################*/ // Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only. LLProxy(); - // Static check for enabled status for UDP packets. Call from main thread only. - static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; } + // Static check for enabled status for UDP packets. Called from main thread only. + static bool isSOCKSProxyEnabled(void) { llassert(is_main_thread()); return sUDPProxyEnabled; } - // Get the UDP proxy address and port. Call from main thread only. - LLHost getUDPProxy() const { return mUDPProxy; } + // Get the UDP proxy address and port. Called from main thread only. + LLHost getUDPProxy(void) const { return Unshared_crat(mUnshared)->mUDPProxy; } /*########################################################################################### - END OF NON-LOCKING METHODS + End of methods that only access variables not shared between threads. ###########################################################################################*/ + // Return true if there is a good chance that the HTTP proxy is currently enabled. + bool HTTPProxyEnabled(void) const { return mHTTPProxyEnabled; } + /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Public methods that access variables shared between threads. ###########################################################################################*/ // Destructor, closes open connections. Do not call directly, use cleanupClass(). ~LLProxy(); @@ -263,30 +323,37 @@ public: bool enableHTTPProxy(); // Stop proxying HTTP packets. Call from main thread only. - void disableHTTPProxy(); + // Note that this needs shared_w to be passed because we want the shared members to be locked when this is reset to false. + void disableHTTPProxy(Shared_wat const& shared_w) { mHTTPProxyEnabled = false; } + void disableHTTPProxy(void) { disableHTTPProxy(Shared_wat(mShared)); } + + // Get the currently selected HTTP proxy address and port + LLHost const& getHTTPProxy(Shared_crat const& shared_r) const { return shared_r->mHTTPProxy; } + + // Get the currently selected HTTP proxy type + LLHttpProxyType getHTTPProxyType(Shared_crat const& shared_r) const { return shared_r->mProxyType; } + + // Get the currently selected auth method. + LLSocks5AuthType getSelectedAuthMethod(Shared_crat const& shared_r) const { return shared_r->mAuthMethodSelected; } + + // SOCKS 5 username and password accessors. + std::string getSocksUser(Shared_crat const& shared_r) const { return shared_r->mSocksUsername; } + std::string getSocksPwd(Shared_crat const& shared_r) const { return shared_r->mSocksPassword; } /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ + private: /*########################################################################################### - METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED! + Private methods that access variables shared between threads. ###########################################################################################*/ // Perform a SOCKS 5 authentication and UDP association with the proxy server. S32 proxyHandshake(LLHost proxy); - // Get the currently selected auth method. - LLSocks5AuthType getSelectedAuthMethod() const; - - // Get the currently selected HTTP proxy type - LLHttpProxyType getHTTPProxyType() const; - - std::string getSocksPwd() const; - std::string getSocksUser() const; - /*########################################################################################### - END OF LOCKING METHODS + End of methods that access variables shared between threads. ###########################################################################################*/ private: @@ -294,49 +361,16 @@ private: // Instead use enableHTTPProxy() and disableHTTPProxy() instead. mutable LLAtomic32 mHTTPProxyEnabled; - // Mutex to protect shared members in non-main thread calls to applyProxySettings(). - mutable LLMutex mProxyMutex; - - /*########################################################################################### - MEMBERS READ AND WRITTEN ONLY IN THE MAIN THREAD. DO NOT SHARE! - ###########################################################################################*/ - // Is the UDP proxy enabled? static bool sUDPProxyEnabled; - // UDP proxy address and port - LLHost mUDPProxy; - // TCP proxy control channel address and port - LLHost mTCPProxy; + AIThreadSafeSingleThreadDC mUnshared; + AIThreadSafeDC mShared; - // socket handle to proxy TCP control channel - LLSocket::ptr_t mProxyControlChannel; - - /*########################################################################################### - END OF UNSHARED MEMBERS - ###########################################################################################*/ - - /*########################################################################################### - MEMBERS WRITTEN IN MAIN THREAD AND READ IN ANY THREAD. ONLY READ OR WRITE AFTER LOCKING mProxyMutex! - ###########################################################################################*/ - - // HTTP proxy address and port - LLHost mHTTPProxy; - - // Currently selected HTTP proxy type. Can be web or socks. - LLHttpProxyType mProxyType; - - // SOCKS 5 selected authentication method. - LLSocks5AuthType mAuthMethodSelected; - - // SOCKS 5 username - std::string mSocksUsername; - // SOCKS 5 password - std::string mSocksPassword; - - /*########################################################################################### - END OF SHARED MEMBERS - ###########################################################################################*/ +public: + // For thread-safe read access. Use the _crat access types with these. + AIThreadSafeSingleThreadDC const& unshared_lockobj(void) const { return mUnshared; } + AIThreadSafeDC const& shared_lockobj(void) const { return mShared; } }; #endif From fef461fd13cdaae63066bbc0cf5a4695294d520c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 25 Jun 2012 15:18:07 -0400 Subject: [PATCH 003/123] Grabbed openSSL-1.0.0d from upstream for linux, necessary for non-standalone compiles. Also brought in linux64 version I had sitting around, collecting dust. --- indra/newview/viewer_manifest.py | 8 ++++---- install.xml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 8f49f94a9..fc9086286 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -801,10 +801,10 @@ class Linux_i686Manifest(LinuxManifest): self.path("libapr-1.so.0") self.path("libaprutil-1.so.0") self.path("libdb-4.2.so") - self.path("libcrypto.so.0.9.7") + self.path("libcrypto.so.1.0.0") self.path("libexpat.so.1") self.path("libhunspell-1.2.so.0.0.0", "libhunspell-1.2.so.0") - self.path("libssl.so.0.9.7") + self.path("libssl.so.1.0.0") #self.path("libuuid.so.1") self.path("libSDL-1.2.so.0") self.path("libELFIO.so") @@ -836,10 +836,10 @@ class Linux_x86_64Manifest(LinuxManifest): self.path("libapr-1.so.0") self.path("libaprutil-1.so.0") self.path("libdb-4.2.so") - self.path("libcrypto.so.0.9.8") + self.path("libcrypto.so.1.0.0") self.path("libexpat.so.1") self.path("libhunspell-1.2.so.0.0.0", "libhunspell-1.2.so.0") - self.path("libssl.so.0.9.8") + self.path("libssl.so.1.0.0") self.path("libuuid.so", "libuuid.so.1") self.path("libSDL-1.2.so.0") self.path("libELFIO.so") diff --git a/install.xml b/install.xml index a56aac3ad..fd128ea38 100644 --- a/install.xml +++ b/install.xml @@ -1069,16 +1069,16 @@ anguage Infrstructure (CLI) international standard linux md5sum - f219ef07b02e2abb9282345c3a8f2b39 + 613856e3880c5898f9629f9f7eb3545c url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.7c-linux-20080812.tar.bz2 + https://bitbucket.org/Lirusaito/singularityviewer/downloads/openssl-1.0.0d-linux-20110418.tar.bz2 linux64 md5sum - 00b23f28a2457d9dabbaff0b29ee7323 + 5785bec161f1ad3069fa8330ba42f404 url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openSSL-0.9.8g-linux64-20080909.tar.bz2 + https://bitbucket.org/Lirusaito/singularityviewer/downloads/openSSL-1.0.0d-linux64-for-singularity.tar.bz2 windows From 433c7c3f99deea67f1d09f8b75dd88a2e733f90b Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 28 Jun 2012 04:08:25 -0400 Subject: [PATCH 004/123] Spelling fixes and stuff like that to AICurl* and llproxy.* documentations Also removes a duplicate include from llares.cpp Conflicts: indra/llmessage/aicurl.cpp --- indra/llmessage/aicurl.cpp | 20 ++++++++++---------- indra/llmessage/aicurl.h | 22 +++++++++++----------- indra/llmessage/aicurlprivate.h | 14 +++++++------- indra/llmessage/aicurlthread.cpp | 32 ++++++++++++++++---------------- indra/llmessage/aicurlthread.h | 4 ++-- indra/llmessage/llares.cpp | 1 - indra/llmessage/llproxy.cpp | 2 +- indra/llmessage/llproxy.h | 2 +- 8 files changed, 48 insertions(+), 49 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 3ab86f56f..9a60ee52d 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -187,7 +187,7 @@ void ssl_init(void) CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); } -// Cleanup OpenSLL library thread-safety. +// Cleanup OpenSSL library thread-safety. void ssl_cleanup(void) { // Dynamic locks callbacks. @@ -844,7 +844,7 @@ void CurlEasyRequest::applyDefaultOptions(void) setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); // This option forces openssl to use TLS version 1. // The Linden Lab servers don't support later TLS versions, and libopenssl-1.0.1c has - // a bug were renegotion fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), + // a bug where renegotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), // causing the connection to fail completely without this hack. // For a commandline test of the same, observe the difference between: // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug @@ -854,12 +854,12 @@ void CurlEasyRequest::applyDefaultOptions(void) setopt(CURLOPT_NOSIGNAL, 1); // The old code did this for the 'buffered' version, but I think it's nonsense. //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); - // Set the CURL options for either Socks or HTTP proxy. + // Set the CURL options for either SOCKS or HTTP proxy. applyProxySettings(); Debug( if (dc::curl.is_on()) { - setopt(CURLOPT_VERBOSE, 1); // Usefull for debugging. + setopt(CURLOPT_VERBOSE, 1); // Useful for debugging. setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_callback); setopt(CURLOPT_DEBUGDATA, this); } @@ -874,11 +874,11 @@ void CurlEasyRequest::finalizeRequest(std::string const& url) lldebugs << url << llendl; setopt(CURLOPT_HTTPHEADER, mHeaders); setoptString(CURLOPT_URL, url); - // The following line is a bit tricky: we store a pointer to the object without increasing it's reference count. + // The following line is a bit tricky: we store a pointer to the object without increasing its reference count. // Of course we could increment the reference count, but that doesn't help much: if then this pointer would // get "lost" we'd have a memory leak. Either way we must make sure that it is impossible that this pointer // will be used if the object is deleted [In fact, since this is finalizeRequest() and not addRequest(), - // incrementing the reference counter would be wrong: if addRequest is never called then the object is + // incrementing the reference counter would be wrong: if addRequest() is never called then the object is // destroyed shortly after and this pointer is never even used.] // This pointer is used in MultiHandle::check_run_count, which means that addRequest() was called and // the reference counter was increased and the object is being kept alive, see the comments above @@ -953,7 +953,7 @@ CurlResponderBuffer::CurlResponderBuffer() CurlResponderBuffer::~CurlResponderBuffer() { ThreadSafeBufferedCurlEasyRequest* lockobj = get_lockobj(); - AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait till possible callbacks have returned. + AICurlEasyRequest_wat curl_easy_request_w(*lockobj); // Wait 'til possible callbacks have returned. curl_easy_request_w->send_events_to(NULL); curl_easy_request_w->revokeCallbacks(); if (mResponder) @@ -1042,7 +1042,7 @@ size_t CurlResponderBuffer::curlWriteCallback(char* data, size_t size, size_t nm ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); // We need to lock the curl easy request object too, because that lock is used - // to make sure that callbacks and destruction aren't done simulaneously. + // to make sure that callbacks and destruction aren't done simultaneously. AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); AICurlResponderBuffer_wat buffer_w(*lockobj); @@ -1057,7 +1057,7 @@ size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nme ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); // We need to lock the curl easy request object too, because that lock is used - // to make sure that callbacks and destruction aren't done simulaneously. + // to make sure that callbacks and destruction aren't done simultaneously. AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); AICurlResponderBuffer_wat buffer_w(*lockobj); @@ -1078,7 +1078,7 @@ size_t CurlResponderBuffer::curlHeaderCallback(char* data, size_t size, size_t n ThreadSafeBufferedCurlEasyRequest* lockobj = static_cast(user_data); // We need to lock the curl easy request object too, because that lock is used - // to make sure that callbacks and destruction aren't done simulaneously. + // to make sure that callbacks and destruction aren't done simultaneously. AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); AICurlResponderBuffer_wat buffer_w(*lockobj); diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index ffff2a01e..a0ab05408 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -125,19 +125,19 @@ void setCAPath(std::string const& file); // The life cycle of classes derived from this class is as follows: // They are allocated with new on the line where get(), getByteRange() or post() is called, // and the pointer to the allocated object is then put in a reference counting ResponderPtr. -// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in it's -// member mResponder. Hence, the life time of a Responder is never longer than it's -// associated CurlResponderBuffer, however, if everything works correct, then normally a +// This ResponderPtr is passed to CurlResponderBuffer::prepRequest which stores it in its +// member mResponder. Hence, the life time of a Responder is never longer than its +// associated CurlResponderBuffer, however, if everything works correctly, then normally a // responder is deleted in CurlResponderBuffer::removed_from_multi_handle by setting // mReponder to NULL. // -// Note that the life time of CurlResponderBuffer is (a bit) shorter than the associated +// Note that the lifetime of CurlResponderBuffer is (a bit) shorter than the associated // CurlEasyRequest (because of the order of base classes of ThreadSafeBufferedCurlEasyRequest) // and the callbacks, as set by prepRequest, only use those two. // A callback locks the CurlEasyRequest before actually making the callback, and the // destruction of CurlResponderBuffer also first locks the CurlEasyRequest, and then revokes // the callbacks. This assures that a Responder is never used when the objects it uses are -// destructed. Also, if any of those are destructed then the responder is automatically +// destructed. Also, if any of those are destructed then the Responder is automatically // destructed too. // class Responder { @@ -150,8 +150,8 @@ class Responder { std::string mURL; public: - // Called to set the url of the current request for this responder, - // used only when printing debug output regarding activity of the responder. + // Called to set the URL of the current request for this Responder, + // used only when printing debug output regarding activity of the Responder. void setURL(std::string const& url); public: @@ -193,7 +193,7 @@ class Responder { public: // Called from LLSDMessage::ResponderAdapter::listener. - // LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these function needs to be public. + // LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these functions need to be public. void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); } void pubResult(LLSD const& content) { result(content); } @@ -246,12 +246,12 @@ struct AICurlEasyHandleEvents { // Therefore we use the following trick: we wrap CurlEasyRequestPtr too, and only allow // read accesses on it. -// AICurlEasyRequest: a thread safe, reference counting, auto cleaning curl easy handle. +// AICurlEasyRequest: a thread safe, reference counting, auto-cleaning curl easy handle. class AICurlEasyRequest { public: // Initial construction is allowed (thread-safe). // Note: If ThreadSafeCurlEasyRequest() throws then the memory allocated is still freed. - // 'new' never returned however and the constructor nor destructor of mCurlEasyRequest is called in this case. + // 'new' never returned however and neither the constructor nor destructor of mCurlEasyRequest is called in this case. // This might throw AICurlNoEasyHandle. AICurlEasyRequest(bool buffered) : mCurlEasyRequest(buffered ? new AICurlPrivate::ThreadSafeBufferedCurlEasyRequest : new AICurlPrivate::ThreadSafeCurlEasyRequest) { } @@ -295,7 +295,7 @@ class AICurlEasyRequest { // If we have a correct (with regard to reference counting) AICurlPrivate::CurlEasyRequestPtr, // then it's OK to construct a AICurlEasyRequest from it. - // Note that the external AICurlPrivate::CurlEasyRequestPtr needs it's own locking, because + // Note that the external AICurlPrivate::CurlEasyRequestPtr needs its own locking, because // it's not thread-safe in itself. AICurlEasyRequest(AICurlPrivate::CurlEasyRequestPtr const& ptr) : mCurlEasyRequest(ptr) { } diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index fcf51e927..c5843176d 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -97,7 +97,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); public: - // Retuns total number of existing CURL* handles (excluding ones created outside this class). + // Returns total number of existing CURL* handles (excluding ones created outside this class). static U32 getTotalEasyHandles(void) { return sTotalEasyHandles; } // Returns true if this easy handle was added to a curl multi handle. @@ -114,7 +114,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; } protected: - // Return the underlaying curl easy handle. + // Return the underlying curl easy handle. CURL* getEasyHandle(void) const { return mEasyHandle; } private: @@ -149,7 +149,7 @@ class CurlEasyRequest : public CurlEasyHandle { void addHeader(char const* str); private: - // Call back stubs. + // Callback stubs. static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* userdata); static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* userdata); static size_t readCallback(char* ptr, size_t size, size_t nmemb, void* userdata); @@ -217,7 +217,7 @@ class CurlEasyRequest : public CurlEasyHandle { ~CurlEasyRequest(); public: - // Post initialization, set the parent to which to pass the events to. + // Post-initialization, set the parent to pass the events to. void send_events_to(AICurlEasyHandleEvents* target) { mEventsTarget = target; } // For debugging purposes @@ -237,7 +237,7 @@ class CurlEasyRequest : public CurlEasyHandle { // Curl callbacks write into and read from these buffers. // The interface with the rest of the code is through AICurlInterface::Responder. // -// The life time of a CurlResponderBuffer is slightly shorter than it's +// The lifetime of a CurlResponderBuffer is slightly shorter than its // associated CurlEasyRequest; this class can only be created as base class // of ThreadSafeBufferedCurlEasyRequest, and is therefore constructed after // the construction of the associated CurlEasyRequest and destructed before it. @@ -311,8 +311,8 @@ class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple { friend void intrusive_ptr_release(ThreadSafeCurlEasyRequest* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr is destroyed. }; -// Same as the above but adds a CurlResponderBuffer. The latter has it's own locking in order to -// allow casting the underlaying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of +// Same as the above but adds a CurlResponderBuffer. The latter has its own locking in order to +// allow casting the underlying CurlEasyRequest to ThreadSafeCurlEasyRequest, independent of // what class it is part of: ThreadSafeCurlEasyRequest or ThreadSafeBufferedCurlEasyRequest. // The virtual destructor of ThreadSafeCurlEasyRequest allows to treat each easy handle transparently // as a ThreadSafeCurlEasyRequest object, or optionally dynamic_cast it to a ThreadSafeBufferedCurlEasyRequest. diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 6b2d1d68d..7e51ee5b5 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -115,9 +115,9 @@ namespace curlthread { //----------------------------------------------------------------------------- // PollSet -// A PollSet can store at least 1024 file descriptors, or FD_SETSIZE if that is larger than 1024 [MAXSIZE]. -// The number of stored file descriptors is mNrFds [0 <= mNrFds <= MAXSIZE]. -// The largest file descriptor is stored is mMaxFd, which is -1 iff mNrFds == 0. +// A PollSet can store at least 1024 filedescriptors, or FD_SETSIZE if that is larger than 1024 [MAXSIZE]. +// The number of stored filedescriptors is mNrFds [0 <= mNrFds <= MAXSIZE]. +// The largest filedescriptor is stored is mMaxFd, which is -1 iff mNrFds == 0. // The file descriptors are stored contiguous in mFileDescriptors[i], with 0 <= i < mNrFds. // File descriptors with the highest priority should be stored first (low index). // @@ -126,7 +126,7 @@ namespace curlthread { // // After a call to refresh(): // -// mFdSet has bits set for at most FD_SETSIZE - 1 file descriptors, copied from mFileDescriptors starting +// mFdSet has bits set for at most FD_SETSIZE - 1 filedescriptors, copied from mFileDescriptors starting // at index mNext (wrapping around to 0). If mNrFds < FD_SETSIZE then mNext is reset to 0 before copying starts. // If mNrFds >= FD_SETSIZE then mNext is set to the next filedescriptor that was not copied (otherwise it is left at 0). // @@ -139,7 +139,7 @@ PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [std::max(1024, FD_S FD_ZERO(&mFdSet); } -// Add file descriptor s to the PollSet. +// Add filedescriptor s to the PollSet. void PollSet::add(curl_socket_t s) { llassert_always(mNrFds < mSize); @@ -147,7 +147,7 @@ void PollSet::add(curl_socket_t s) mMaxFd = std::max(mMaxFd, s); } -// Remove file descriptor s from the PollSet. +// Remove filedescriptor s from the PollSet. void PollSet::remove(curl_socket_t s) { // The number of open filedescriptors is relatively small, @@ -220,8 +220,8 @@ refresh_t PollSet::refresh(void) llassert_always(mNext < mNrFds); - // Test if mNrFds is larger or equal FD_SETSIZE; equal, because we reserve one - // file descriptor for the wakeup fd: we copy maximal FD_SETSIZE - 1 file descriptors. + // Test if mNrFds is larger than or equal FD_SETSIZE; equal, because we reserve one + // filedescriptor for the wakeup fd: we copy maximal FD_SETSIZE - 1 filedescriptors. // If not then we're going to copy everything so that we can save on CPU cycles // by not calculating mMaxFdSet here. if (mNrFds >= FD_SETSIZE) @@ -278,8 +278,8 @@ refresh_t PollSet::refresh(void) return complete_not_empty; } -// FIXME: This needs a rewrite on windows, as FD_ISSET is slow there; it would make -// more sense to iterate directly over the fd's in mFdSet on windows. +// FIXME: This needs a rewrite on Windows, as FD_ISSET is slow there; it would make +// more sense to iterate directly over the fd's in mFdSet on Windows. void PollSet::reset(void) { llassert((mNrFds == 0) == mCopiedFileDescriptors.empty()); @@ -298,8 +298,8 @@ inline int PollSet::get(void) const return (mIter == mCopiedFileDescriptors.end()) ? -1 : *mIter; } -// FIXME: This needs a rewrite on windows, as FD_ISSET is slow there; it would make -// more sense to iterate directly over the fd's in mFdSet on windows. +// FIXME: This needs a rewrite on Windows, as FD_ISSET is slow there; it would make +// more sense to iterate directly over the fd's in mFdSet on Windows. void PollSet::next(void) { llassert(mIter != mCopiedFileDescriptors.end()); // Only call next() if the last call to get() didn't return -1. @@ -476,7 +476,7 @@ AICurlThread::~AICurlThread() void AICurlThread::create_wakeup_fds(void) { #ifdef WINDOWS -// Probably need to use sockets here, cause windows select doesn't work for a pipe. +// Probably need to use sockets here, cause Windows select doesn't work for a pipe. #error Missing implementation #else int pipefd[2]; @@ -626,7 +626,7 @@ void AICurlThread::run(void) int nfds = std::max(max_rfd, max_wfd) + 1; llassert(0 <= nfds && nfds <= FD_SETSIZE); llassert((max_rfd == -1) == (read_fd_set == NULL) && - (max_wfd == -1) == (write_fd_set == NULL)); // Needed on windows. + (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); int ready = 0; @@ -971,7 +971,7 @@ void AICurlEasyRequest::addRequest(void) command_being_processed_rat command_being_processed_r(command_being_processed); if (*command_being_processed_r == *this) { - // May not be inbetween being removed from the command queue but not added to the multi session handle yet. + // May not be in-between being removed from the command queue but not added to the multi session handle yet. llassert(command_being_processed_r->command() == cmd_remove); } else @@ -1020,7 +1020,7 @@ void AICurlEasyRequest::removeRequest(void) command_being_processed_rat command_being_processed_r(command_being_processed); if (*command_being_processed_r == *this) { - // May not be inbetween being removed from the command queue but not removed from the multi session handle yet. + // May not be in-between being removed from the command queue but not removed from the multi session handle yet. llassert(command_being_processed_r->command() != cmd_remove); } else diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index 448e53ac0..e8b080b94 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -100,7 +100,7 @@ class PollSet fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). int mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or -1 when it was empty. - std::vector mCopiedFileDescriptors; // File descriptors copied by refresh to mFdSet. + std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. }; @@ -136,7 +136,7 @@ class MultiHandle : public CurlMultiHandle bool mHandleAddedOrRemoved; // Set when an easy handle was added or removed, reset in check_run_count(). int mPrevRunningHandles; // The last value of mRunningHandles that check_run_count() was called with. int mRunningHandles; // The last value returned by curl_multi_socket_action. - long mTimeOut; // The last time out in ms as set by the call back CURLMOPT_TIMERFUNCTION. + long mTimeOut; // The last time out in ms as set by the callback CURLMOPT_TIMERFUNCTION. private: static int socket_callback(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index 3328f2fd5..d83cd6a37 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -28,7 +28,6 @@ #include "linden_common.h" #include "llares.h" -#include "llscopedvolatileaprpool.h" #include #include diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index a82ae3cc7..d46c70f04 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -179,7 +179,7 @@ S32 LLProxy::proxyHandshake(LLHost proxy) } { - // Write acccess type and read access type are really the same, so unshared_w must be simply a reference. + // Write access type and read access type are really the same, so unshared_w must be simply a reference. Unshared_wat& unshared_w = unshared_r; unshared_w->mUDPProxy.setPort(ntohs(connect_reply.port)); // reply port is in network byte order unshared_w->mUDPProxy.setAddress(proxy.getAddress()); diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index c0d6ab183..00bfa1ad4 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -244,7 +244,7 @@ struct ProxyShared // HTTP proxy address and port LLHost mHTTPProxy; - // Currently selected HTTP proxy type. Can be web or socks. + // Currently selected HTTP proxy type. Can be web or SOCKS. LLHttpProxyType mProxyType; // SOCKS 5 selected authentication method. From 2dee921cd544d6c8efa1655ceb8081c850b8bf97 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 29 Jun 2012 01:33:38 +0200 Subject: [PATCH 005/123] Fix libcurl version check. --- indra/llmessage/aicurl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 3ab86f56f..6dc43cc2c 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -204,6 +204,10 @@ void ssl_cleanup(void) } // namespace openSSL //----------------------------------------------------------------------------------- +static unsigned int encoded_version(int major, int minor, int patch) +{ + return (major << 16) | (minor << 8) | patch; +} //================================================================================== // External API @@ -283,7 +287,8 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles) } } - gSetoptParamsNeedDup = (version_info->version_num < 0x071700); + // Before version 7.17.0, strings were not copied. Instead the user was forced keep them available until libcurl no longer needed them. + gSetoptParamsNeedDup = (version_info->version_num < encoded_version(7, 17, 0)); if (gSetoptParamsNeedDup) { llwarns << "Your libcurl version is too old." << llendl; From 90493b65712a769276835691af6549ca90b69e63 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 29 Jun 2012 05:20:24 +0200 Subject: [PATCH 006/123] Add support for libopenSSL older than version 1.0.0. --- indra/llmessage/aicurl.cpp | 50 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6dc43cc2c..abba39c45 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -83,6 +83,13 @@ bool gSetoptParamsNeedDup; } // namespace +// See http://www.openssl.org/docs/crypto/threads.html: +// CRYPTO_THREADID and associated functions were introduced in OpenSSL 1.0.0 to replace +// (actually, deprecate) the previous CRYPTO_set_id_callback(), CRYPTO_get_id_callback(), +// and CRYPTO_thread_id() functions which assumed thread IDs to always be represented by +// 'unsigned long'. +#define HAVE_CRYPTO_THREADID (OPENSSL_VERSION_NUMBER >= (1 << 28)) + //----------------------------------------------------------------------------------- // Needed for thread-safe openSSL operation. @@ -115,6 +122,7 @@ void ssl_locking_function(int mode, int n, char const* file, int line) } } +#if HAVE_CRYPTO_THREADID // OpenSSL uniq id function. void ssl_id_function(CRYPTO_THREADID* thread_id) { @@ -124,6 +132,7 @@ void ssl_id_function(CRYPTO_THREADID* thread_id) CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); #endif } +#endif // HAVE_CRYPTO_THREADID // OpenSSL allocate and initialize dynamic crypto lock. CRYPTO_dynlock_value* ssl_dyn_create_function(char const* file, int line) @@ -157,13 +166,21 @@ void ssl_dyn_lock_function(int mode, CRYPTO_dynlock_value* l, char const* file, } typedef void (*ssl_locking_function_type)(int, int, char const*, int); +#if HAVE_CRYPTO_THREADID typedef void (*ssl_id_function_type)(CRYPTO_THREADID*); +#else +typedef unsigned long (*ulong_thread_id_function_type)(void); +#endif typedef CRYPTO_dynlock_value* (*ssl_dyn_create_function_type)(char const*, int); typedef void (*ssl_dyn_destroy_function_type)(CRYPTO_dynlock_value*, char const*, int); typedef void (*ssl_dyn_lock_function_type)(int, CRYPTO_dynlock_value*, char const*, int); ssl_locking_function_type old_ssl_locking_function; +#if HAVE_CRYPTO_THREADID ssl_id_function_type old_ssl_id_function; +#else +ulong_thread_id_function_type old_ulong_thread_id_function; +#endif ssl_dyn_create_function_type old_ssl_dyn_create_function; ssl_dyn_destroy_function_type old_ssl_dyn_destroy_function; ssl_dyn_lock_function_type old_ssl_dyn_lock_function; @@ -171,13 +188,36 @@ ssl_dyn_lock_function_type old_ssl_dyn_lock_function; // Initialize OpenSSL library for thread-safety. void ssl_init(void) { + // The version identifier format is: MMNNFFPPS: major minor fix patch status. + int const compiled_openSLL_major = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; + int const compiled_openSLL_minor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; + int const linked_openSLL_major = (SSLeay() >> 28) & 0xff; + int const linked_openSLL_minor = (SSLeay() >> 20) & 0xff; + // Check if dynamically loaded version is compatible with the one we compiled against. + // As off version 1.0.0 also minor versions are compatible. + if (linked_openSLL_major != compiled_openSLL_major || + (compiled_openSLL_major == 0 && linked_openSLL_minor != compiled_openSLL_minor)) + { + llerrs << "The viewer was compiled against " << OPENSSL_VERSION_TEXT << + " but linked against " << SSLeay_version(SSLEAY_VERSION) << + ". Those versions are not compatible." << llendl; + } // Static locks vector. ssl_rwlock_array = new AIRWLock[CRYPTO_num_locks()]; // Static locks callbacks. old_ssl_locking_function = CRYPTO_get_locking_callback(); +#if HAVE_CRYPTO_THREADID old_ssl_id_function = CRYPTO_THREADID_get_callback(); +#else + old_ulong_thread_id_function = CRYPTO_get_id_callback(); +#endif CRYPTO_set_locking_callback(&ssl_locking_function); - CRYPTO_THREADID_set_callback(&ssl_id_function); // Setting this avoids the need for a thread-local error number facility, which is hard to check. + // Setting this avoids the need for a thread-local error number facility, which is hard to check. +#if HAVE_CRYPTO_THREADID + CRYPTO_THREADID_set_callback(&ssl_id_function); +#else + CRYPTO_set_id_callback(&apr_os_thread_current); +#endif // Dynamic locks callbacks. old_ssl_dyn_create_function = CRYPTO_get_dynlock_create_callback(); old_ssl_dyn_lock_function = CRYPTO_get_dynlock_lock_callback(); @@ -185,6 +225,8 @@ void ssl_init(void) CRYPTO_set_dynlock_create_callback(&ssl_dyn_create_function); CRYPTO_set_dynlock_lock_callback(&ssl_dyn_lock_function); CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); + llinfos << "Successful initialization of " << + SSLeay_version(SSLEAY_VERSION) << " (0x" << std::hex << SSLeay() << ")." << llendl; } // Cleanup OpenSLL library thread-safety. @@ -195,7 +237,11 @@ void ssl_cleanup(void) CRYPTO_set_dynlock_lock_callback(old_ssl_dyn_lock_function); CRYPTO_set_dynlock_create_callback(old_ssl_dyn_create_function); // Static locks callbacks. +#if HAVE_CRYPTO_THREADID CRYPTO_THREADID_set_callback(old_ssl_id_function); +#else + CRYPTO_set_id_callback(old_ulong_thread_id_function); +#endif CRYPTO_set_locking_callback(old_ssl_locking_function); // Static locks vector. delete [] ssl_rwlock_array; @@ -246,7 +292,7 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles) } llinfos << "Successful initialization of libcurl " << - version_info->version << " (" << version_info->version_num << "), (" << + version_info->version << " (0x" << std::hex << version_info->version_num << "), (" << version_info->ssl_version << ", libz/" << version_info->libz_version << ")." << llendl; // Detect SSL library used. From cb5efad026c8d97587e8ce358118c877fc579a39 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 30 Jun 2012 15:41:24 +0200 Subject: [PATCH 007/123] Turn llassert[_always] into a (single) statement and print line nr in decimal. --- indra/llcommon/llerrorlegacy.h | 2 +- indra/llcommon/llqueuedthread.cpp | 2 +- indra/llmessage/llassetstorage.cpp | 4 ++-- indra/newview/llmeshrepository.cpp | 2 +- indra/newview/llspatialpartition.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h index fa41c5b3d..278f781ae 100644 --- a/indra/llcommon/llerrorlegacy.h +++ b/indra/llcommon/llerrorlegacy.h @@ -114,7 +114,7 @@ const int LL_ERR_PRICE_MISMATCH = -23018; : liru_slashpos2 == std::string::npos ? std::string(__FILE__)/*Apparently, we're in / or perhaps the top of the drive, print as is*/\ : std::string(__FILE__).substr(1+liru_slashpos2))/*print foo/bar.cpp or perhaps foo\bar.cpp*/ -#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs <<"\nASSERT(" #func ")\nfile:"<getExists(uuid, type); @@ -441,7 +441,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL if (user_data) { // The *user_data should not be passed without a callback to clean it up. - llassert(callback != NULL) + llassert(callback != NULL); } if (mShutDown) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index cd44d3db9..e949460c5 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -680,7 +680,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (pending != mPendingLOD.end()) { //append this lod request to existing header request pending->second.push_back(lod); - llassert(pending->second.size() <= LLModel::NUM_LODS) + llassert(pending->second.size() <= LLModel::NUM_LODS); } else { //if no header request is pending, fetch header diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index db0e490cf..f4cde0074 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2007,7 +2007,7 @@ public: virtual void processGroup(LLSpatialGroup* group) { - llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty()) + llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty()); if (mRes < 2) { From a803507d67bc7ff3d7f137a85d86017a254ab0de Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 1 Jul 2012 22:15:03 +0200 Subject: [PATCH 008/123] Use correct way to check if we logged in yet or not. Without this fix, we trigger an assert, in debug mode, that was added to Singularity exactly to find out if we called functions like getExpandedFilename(LL_PATH_PER_SL_ACCOUNT ...) before logging in. Checking if THAT function returns empty() is clearly not safe, but very error prone. --- indra/newview/llviewertexturelist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 4be8df0e9..d685b951e 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -277,7 +277,7 @@ void LLViewerTextureList::shutdown() break; } - if (count > 0 && !gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "").empty()) + if (count > 0 && !gDirUtilp->getLindenUserDir(true).empty()) { std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); llofstream file; From 125a10bb447b8ab693d6c526b296ea95467e1ab9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 4 Jul 2012 00:10:43 +0200 Subject: [PATCH 009/123] Code hardening, review, bug fixes, documentation, curl stats and cleanup. Bug fixes: AICurlEasyRequestStateMachine didn't delete itself. curl_multi_socket_action calls were made for potentional removed sockets. The curl thread wasn't terminated. --- indra/llmessage/aicurl.cpp | 107 ++++--- indra/llmessage/aicurlprivate.h | 42 ++- indra/llmessage/aicurlthread.cpp | 276 ++++++++++++------ indra/llmessage/aicurlthread.h | 2 +- indra/newview/llxmlrpctransaction.cpp | 14 +- .../aicurleasyrequeststatemachine.cpp | 2 + 6 files changed, 300 insertions(+), 143 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 5a012c0e7..a2ea765f1 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -259,10 +259,9 @@ static unsigned int encoded_version(int major, int minor, int patch) // External API // -namespace AICurlInterface { - #undef AICurlPrivate -using AICurlPrivate::check_easy_code; + +namespace AICurlInterface { // MAIN-THREAD void initCurl(F32 curl_request_timeout, S32 max_number_handles) @@ -270,7 +269,7 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles) DoutEntering(dc::curl, "AICurlInterface::initCurl(" << curl_request_timeout << ", " << max_number_handles << ")"); llassert(LLThread::getRunning() == 0); // We must not call curl_global_init unless we are the only thread. - CURLcode res = check_easy_code(curl_global_init(CURL_GLOBAL_ALL)); + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); if (res != CURLE_OK) { llerrs << "curl_global_init(CURL_GLOBAL_ALL) failed." << llendl; @@ -346,10 +345,12 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles) // MAIN-THREAD void cleanupCurl(void) { + using AICurlPrivate::stopCurlThread; using AICurlPrivate::curlThreadIsRunning; DoutEntering(dc::curl, "AICurlInterface::cleanupCurl()"); + stopCurlThread(); ssl_cleanup(); llassert(LLThread::getRunning() <= (curlThreadIsRunning() ? 1 : 0)); // We must not call curl_global_cleanup unless we are the only thread left. @@ -495,30 +496,32 @@ void intrusive_ptr_release(Responder* responder) namespace AICurlPrivate { -// THREAD-SAFE -CURLcode check_easy_code(CURLcode code) +//static +LLAtomicU32 Stats::easy_calls; +LLAtomicU32 Stats::easy_errors; +LLAtomicU32 Stats::easy_init_calls; +LLAtomicU32 Stats::easy_init_errors; +LLAtomicU32 Stats::easy_cleanup_calls; +LLAtomicU32 Stats::multi_calls; +LLAtomicU32 Stats::multi_errors; + +//static +void Stats::print(void) { - if (code != CURLE_OK) - { - char* error_buffer = LLThreadLocalData::tldata().mCurlErrorBuffer; - llinfos << "curl easy error detected: " << curl_easy_strerror(code); - if (error_buffer && *error_buffer != '\0') - { - llcont << ": " << error_buffer; - } - llcont << llendl; - } - return code; + llinfos << "====== CURL STATS ======" << llendl; + llinfos << " Curl multi errors/calls: " << std::dec << multi_errors << "/" << multi_calls << llendl; + llinfos << " Curl easy errors/calls: " << std::dec << easy_errors << "/" << easy_calls << llendl; + llinfos << " curl_easy_init() errors/calls: " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; + llinfos << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl; + llinfos << "=== END OF CURL STATS ===" << llendl; } // THREAD-SAFE -CURLMcode check_multi_code(CURLMcode code) +void handle_multi_error(CURLMcode code) { - if (code != CURLM_OK) - { - llinfos << "curl multi error detected: " << curl_multi_strerror(code) << llendl; - } - return code; + Stats::multi_errors++; + llinfos << "curl multi error detected: " << curl_multi_strerror(code) << + "; (errors/calls = " << Stats::multi_errors << "/" << Stats::multi_calls << ")" << llendl; } //============================================================================= @@ -528,33 +531,46 @@ CURLMcode check_multi_code(CURLMcode code) //----------------------------------------------------------------------------- // CurlEasyHandle -LLAtomicU32 CurlEasyHandle::sTotalEasyHandles; +// THREAD-SAFE +//static +void CurlEasyHandle::handle_easy_error(CURLcode code) +{ + char* error_buffer = LLThreadLocalData::tldata().mCurlErrorBuffer; + llinfos << "curl easy error detected: " << curl_easy_strerror(code); + if (error_buffer && *error_buffer != '\0') + { + llcont << ": " << error_buffer; + } + Stats::easy_errors++; + llcont << "; (errors/calls = " << Stats::easy_errors << "/" << Stats::easy_calls << ")" << llendl; +} // Throws AICurlNoEasyHandle. CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) { mEasyHandle = curl_easy_init(); #if 0 - //FIXME: for debugging, throw once every 10 times. - static int c = 0; - if (++c % 10 == 5) + // Fake curl_easy_init() failures: throw once every 10 times (for debugging purposes). + static int count = 0; + if (mEasyHandle && (++count % 10) == 5) { curl_easy_cleanup(mEasyHandle); mEasyHandle = NULL; } #endif + Stats::easy_init_calls++; if (!mEasyHandle) { + Stats::easy_init_errors++; throw AICurlNoEasyHandle("curl_easy_init() returned NULL"); } - sTotalEasyHandles++; } CurlEasyHandle::~CurlEasyHandle() { llassert(!mActiveMultiHandle); curl_easy_cleanup(mEasyHandle); - --sTotalEasyHandles; + Stats::easy_cleanup_calls++; } //static @@ -573,8 +589,13 @@ void CurlEasyHandle::setErrorBuffer(void) char* error_buffer = getTLErrorBuffer(); if (mErrorBuffer != error_buffer) { - curl_easy_setopt(mEasyHandle, CURLOPT_ERRORBUFFER, error_buffer); mErrorBuffer = error_buffer; + CURLcode res = curl_easy_setopt(mEasyHandle, CURLOPT_ERRORBUFFER, error_buffer); + if (res != CURLE_OK) + { + llwarns << "curl_easy_setopt(" << (void*)mEasyHandle << "CURLOPT_ERRORBUFFER, " << (void*)error_buffer << ") failed with error " << res << llendl; + mErrorBuffer = NULL; + } } } @@ -727,27 +748,29 @@ void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* us setopt(CURLOPT_SSL_CTX_DATA, userdata ? this : NULL); } +#define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false) + static size_t noHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { - llwarns << "Calling noHeaderCallback(); curl session aborted." << llendl; + llmaybewarns << "Calling noHeaderCallback(); curl session aborted." << llendl; return 0; // Cause a CURL_WRITE_ERROR } static size_t noWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { - llwarns << "Calling noWriteCallback(); curl session aborted." << llendl; + llmaybewarns << "Calling noWriteCallback(); curl session aborted." << llendl; return 0; // Cause a CURL_WRITE_ERROR } static size_t noReadCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { - llwarns << "Calling noReadCallback(); curl session aborted." << llendl; + llmaybewarns << "Calling noReadCallback(); curl session aborted." << llendl; return CURL_READFUNC_ABORT; // Cause a CURLE_ABORTED_BY_CALLBACK } static CURLcode noSSLCtxCallback(CURL* curl, void* sslctx, void* parm) { - llwarns << "Calling noSSLCtxCallback(); curl session aborted." << llendl; + llmaybewarns << "Calling noSSLCtxCallback(); curl session aborted." << llendl; return CURLE_ABORTED_BY_CALLBACK; } @@ -765,7 +788,7 @@ void CurlEasyRequest::revokeCallbacks(void) mWriteCallback = &noWriteCallback; mReadCallback = &noReadCallback; mSSLCtxCallback = &noSSLCtxCallback; - if (active()) + if (active() && !LLApp::isExiting()) { llwarns << "Revoking callbacks on a still active CurlEasyRequest object!" << llendl; } @@ -907,14 +930,17 @@ void CurlEasyRequest::applyDefaultOptions(void) //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); // Set the CURL options for either SOCKS or HTTP proxy. applyProxySettings(); +#if 0 + // Cause libcurl to print all it's I/O traffic on the debug channel. Debug( if (dc::curl.is_on()) { - setopt(CURLOPT_VERBOSE, 1); // Useful for debugging. + setopt(CURLOPT_VERBOSE, 1); setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_callback); setopt(CURLOPT_DEBUGDATA, this); } ); +#endif } void CurlEasyRequest::finalizeRequest(std::string const& url) @@ -1204,9 +1230,12 @@ LLAtomicU32 CurlMultiHandle::sTotalMultiHandles; CurlMultiHandle::CurlMultiHandle(void) { + DoutEntering(dc::curl, "CurlMultiHandle::CurlMultiHandle() [" << (void*)this << "]."); mMultiHandle = curl_multi_init(); + Stats::multi_calls++; if (!mMultiHandle) { + Stats::multi_errors++; throw AICurlNoMultiHandle("curl_multi_init() returned NULL"); } sTotalMultiHandles++; @@ -1215,7 +1244,11 @@ CurlMultiHandle::CurlMultiHandle(void) CurlMultiHandle::~CurlMultiHandle() { curl_multi_cleanup(mMultiHandle); - --sTotalMultiHandles; + Stats::multi_calls++; + int total = --sTotalMultiHandles; + Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining."); + if (total == 0) + Stats::print(); } } // namespace AICurlPrivate diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index c5843176d..82d4e9c44 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -32,15 +32,29 @@ #define AICURLPRIVATE_H #include +#include "llatomic.h" namespace AICurlPrivate { namespace curlthread { class MultiHandle; } -CURLcode check_easy_code(CURLcode code); -CURLMcode check_multi_code(CURLMcode code); +struct Stats { + static LLAtomicU32 easy_calls; + static LLAtomicU32 easy_errors; + static LLAtomicU32 easy_init_calls; + static LLAtomicU32 easy_init_errors; + static LLAtomicU32 easy_cleanup_calls; + static LLAtomicU32 multi_calls; + static LLAtomicU32 multi_errors; + + static void print(void); +}; + +void handle_multi_error(CURLMcode code); +inline CURLMcode check_multi_code(CURLMcode code) { Stats::multi_calls++; if (code != CURLM_OK) handle_multi_error(code); return code; } bool curlThreadIsRunning(void); void wakeUpCurlThread(void); +void stopCurlThread(void); class ThreadSafeCurlEasyRequest; class ThreadSafeBufferedCurlEasyRequest; @@ -88,7 +102,6 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURL* mEasyHandle; CURLM* mActiveMultiHandle; char* mErrorBuffer; - static LLAtomicU32 sTotalEasyHandles; private: // This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle. @@ -97,22 +110,31 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURLMcode remove_handle_from_multi(AICurlEasyRequest_wat& curl_easy_request_w, CURLM* multi_handle); public: - // Returns total number of existing CURL* handles (excluding ones created outside this class). - static U32 getTotalEasyHandles(void) { return sTotalEasyHandles; } - // Returns true if this easy handle was added to a curl multi handle. bool active(void) const { return mActiveMultiHandle; } - // Call this prior to every curl_easy function whose return value is passed to check_easy_code. - void setErrorBuffer(void); - // If there was an error code as result, then this returns a human readable error string. // Only valid when setErrorBuffer was called and the curl_easy function returned an error. - std::string getErrorString(void) const { return mErrorBuffer; } + std::string getErrorString(void) const { return mErrorBuffer ? mErrorBuffer : "(null)"; } // Used for debugging purposes. bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; } + private: + // Call this prior to every curl_easy function whose return value is passed to check_easy_code. + void setErrorBuffer(void); + + static void handle_easy_error(CURLcode code); + + // Always first call setErrorBuffer()! + static inline CURLcode check_easy_code(CURLcode code) + { + Stats::easy_calls++; + if (code != CURLE_OK) + handle_easy_error(code); + return code; + } + protected: // Return the underlying curl easy handle. CURL* getEasyHandle(void) const { return mEasyHandle; } diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 7e51ee5b5..005e4c620 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -132,8 +132,10 @@ namespace curlthread { // // mMaxFdSet is the largest filedescriptor in mFdSet or -1 if it is empty. +static size_t const MAXSIZE = std::max(1024, FD_SETSIZE); + // Create an empty PollSet. -PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [std::max(1024, FD_SETSIZE)]), mSize(std::max(1024, FD_SETSIZE)), +PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), mNrFds(0), mMaxFd(-1), mNext(0), mMaxFdSet(-1) { FD_ZERO(&mFdSet); @@ -142,7 +144,7 @@ PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [std::max(1024, FD_S // Add filedescriptor s to the PollSet. void PollSet::add(curl_socket_t s) { - llassert_always(mNrFds < mSize); + llassert_always(mNrFds < MAXSIZE); mFileDescriptors[mNrFds++] = s; mMaxFd = std::max(mMaxFd, s); } @@ -159,17 +161,57 @@ void PollSet::remove(curl_socket_t s) // much clock cycles. Therefore, shift the whole vector // back, keeping it compact and keeping the filedescriptors // in the same order (which is supposedly their priority). + // + // The general case is where mFileDescriptors contains s at an index + // between 0 and mNrFds: + // mNrFds = 6 + // v + // index: 0 1 2 3 4 5 + // a b c s d e + + // This function should never be called unless s is actually in mFileDescriptors, + // as a result of a previous call to PollSet::add(). llassert(mNrFds > 0); + + // Correct mNrFds for when the descriptor is removed. + // Make i 'point' to the last entry. int i = --mNrFds; - int prev = mFileDescriptors[i]; + // i = NrFds = 5 + // v + // index: 0 1 2 3 4 5 + // a b c s d e + int prev = mFileDescriptors[i]; // prev = 'e' int max = -1; for (--i; i >= 0 && prev != s; --i) { - int cur = mFileDescriptors[i]; - mFileDescriptors[i] = prev; - max = std::max(max, prev); - prev = cur; + int cur = mFileDescriptors[i]; // cur = 'd' + mFileDescriptors[i] = prev; // Overwrite 'd' with 'e'. + max = std::max(max, prev); // max is the maximum value in 'i' or higher. + prev = cur; // prev = 'd' + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c s e // prev = 'd' + // + // Next loop iteration: cur = 's', overwrite 's' with 'd', prev = 's'; loop terminates. + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c d e // prev = 's' } + llassert(prev == s); + // At this point i was decremented once more and points to the element before the old s. + // i NrFds = 5 + // v v + // index: 0 1 2 3 4 + // a b c d e // max = std::max('d', 'e') + + // If mNext pointed to an element before s, it should be left alone. Otherwise, if mNext pointed + // to s it must now point to 'd', or if it pointed beyond 's' it must be decremented by 1. + if (mNext > i + 1) // i + 1 is where s was. + --mNext; + + // If s was the largest file descriptor, we have to update mMaxFd. if (s == mMaxFd) { while (i >= 0) @@ -182,8 +224,10 @@ void PollSet::remove(curl_socket_t s) llassert(mMaxFd < s); llassert((mMaxFd == -1) == (mNrFds == 0)); } - if (mNext == mNrFds) - mNext = 0; + + // ALSO make sure that s is no longer set in mFdSet, or we might confuse libcurl by + // calling curl_multi_socket_action for a socket that it told us to remove. + clr(s); } bool PollSet::contains(curl_socket_t fd) const @@ -220,7 +264,7 @@ refresh_t PollSet::refresh(void) llassert_always(mNext < mNrFds); - // Test if mNrFds is larger than or equal FD_SETSIZE; equal, because we reserve one + // Test if mNrFds is larger than or equal to FD_SETSIZE; equal, because we reserve one // filedescriptor for the wakeup fd: we copy maximal FD_SETSIZE - 1 filedescriptors. // If not then we're going to copy everything so that we can save on CPU cycles // by not calculating mMaxFdSet here. @@ -228,22 +272,9 @@ refresh_t PollSet::refresh(void) { llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; // Calculate mMaxFdSet. - int max = -1; - int count = 0; - int end = mNrFds; - int i = mNext; - while (++count < FD_SETSIZE) - { - max = std::max(max, mFileDescriptors[i]); - if (++i == end) - { - if (end == mNext) - break; - end = mNext; - i = 0; - llassert(i < end); // If mNext == 0 then the while loop terminates before we get here. - } - } + // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. + int max = -1, i = mNext, count = 0; + while (++count < FD_SETSIZE) { max = std::max(max, mFileDescriptors[i]); if (++i == mNrFds) i = 0; } mMaxFdSet = max; } else @@ -252,7 +283,6 @@ refresh_t PollSet::refresh(void) mMaxFdSet = mMaxFd; } int count = 0; - int end = mNrFds; int i = mNext; for(;;) { @@ -263,15 +293,13 @@ refresh_t PollSet::refresh(void) } FD_SET(mFileDescriptors[i], &mFdSet); mCopiedFileDescriptors.push_back(mFileDescriptors[i]); - if (++i == end) + if (++i == mNrFds) { + // If we reached the end and start at the beginning, then we copied everything. if (mNext == 0) break; - // If this was true then we got here a second time, which means that we accessed all - // filedescriptors with mNext != 0, but count is still < FD_SETSIZE, which is not - // possible because mNext is only non-zero when mNrFds >= FD_SETSIZE. - llassert(end != mNext); - end = mNext; + // When can only come here if mNrFds >= FD_SETSIZE, hence we can just + // wrap around and terminate on count reaching FD_SETSIZE. i = 0; } } @@ -280,9 +308,29 @@ refresh_t PollSet::refresh(void) // FIXME: This needs a rewrite on Windows, as FD_ISSET is slow there; it would make // more sense to iterate directly over the fd's in mFdSet on Windows. +// +// The API reset(), get() and next() allows one to run over all filedescriptors +// in mFdSet that are set. This works by running only over the filedescriptors +// that were set initially (by the call to refresh()) and then checking if that +// filedescriptor is (still) set in mFdSet. +// +// A call to reset() resets mIter to the beginning, so that get() returns +// the first filedescriptor that is still set. A call to next() progresses +// the iterator to the next set filedescriptor. If get() return -1, then there +// were no more filedescriptors set. +// +// Note that one should never call next() unless get() didn't return -1, so +// the call sequence is: +// refresh(); +// /* reset some or all bits in mFdSet */ +// reset(); +// while (get() != -1) // next(); +// +// Note also that this API is only used by MergeIterator, which wraps it +// and provides a different API to use. + void PollSet::reset(void) { - llassert((mNrFds == 0) == mCopiedFileDescriptors.empty()); if (mCopiedFileDescriptors.empty()) mIter = mCopiedFileDescriptors.end(); else @@ -298,8 +346,6 @@ inline int PollSet::get(void) const return (mIter == mCopiedFileDescriptors.end()) ? -1 : *mIter; } -// FIXME: This needs a rewrite on Windows, as FD_ISSET is slow there; it would make -// more sense to iterate directly over the fd's in mFdSet on Windows. void PollSet::next(void) { llassert(mIter != mCopiedFileDescriptors.end()); // Only call next() if the last call to get() didn't return -1. @@ -308,6 +354,10 @@ void PollSet::next(void) //----------------------------------------------------------------------------- // MergeIterator +// +// This class takes two PollSet's and allows one to run over all filedescriptors +// that are set in one or both poll sets, returning each filedescriptor only +// once, by calling next() until it returns false. class MergeIterator { @@ -343,7 +393,6 @@ bool MergeIterator::next(int& fd_out, int& ev_bitmask_out) fd_out = rfd; ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; mReadPollSet.next(); - } else if ((unsigned int)rfd < (unsigned int)wfd) // Use and increment smaller one, unless it's -1. { @@ -440,6 +489,9 @@ class AICurlThread : public LLThread // MAIN-THREAD void wakeup_thread(void); + // MAIN-THREAD + void stop_thread(void) { mRunning = false; wakeup_thread(); } + protected: virtual void run(void); void wakeup(AICurlMultiHandle_wat const& multi_handle_w); @@ -453,13 +505,15 @@ class AICurlThread : public LLThread int mWakeUpFd; int mZeroTimeOut; + + volatile bool mRunning; }; // Only the main thread is accessing this. AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD -AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(-1), mWakeUpFd(-1), mZeroTimeOut(0) +AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(-1), mWakeUpFd(-1), mZeroTimeOut(0), mRunning(true) { create_wakeup_fds(); sInstance = this; @@ -567,7 +621,7 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; close(mWakeUpFd); mWakeUpFd = -1; - shutdown(); + mRunning = false; return; } #endif @@ -610,30 +664,29 @@ void AICurlThread::run(void) { DoutEntering(dc::curl, "AICurlThread::run()"); - AICurlMultiHandle_wat multi_handle_w(AICurlMultiHandle::getInstance()); - for(;;) { - refresh_t rres = multi_handle_w->mReadPollSet.refresh(); - refresh_t wres = multi_handle_w->mWritePollSet.refresh(); - fd_set* read_fd_set = multi_handle_w->mReadPollSet.access(); - if (LL_LIKELY(mWakeUpFd != -1)) - FD_SET(mWakeUpFd, read_fd_set); - else if ((rres & empty)) - read_fd_set = NULL; - fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); - int const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); - int const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); - int nfds = std::max(max_rfd, max_wfd) + 1; - llassert(0 <= nfds && nfds <= FD_SETSIZE); - llassert((max_rfd == -1) == (read_fd_set == NULL) && - (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. - llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && - (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); - int ready = 0; - if (LL_UNLIKELY(nfds == 0)) // Only happens when the thread is shutting down. - ms_sleep(1000); - else + AICurlMultiHandle_wat multi_handle_w(AICurlMultiHandle::getInstance()); + while(mRunning) { + // If mRunning is true then we can only get here if mWakeUpFd != -1. + llassert(mWakeUpFd != -1); + // Copy the next batch of file descriptors from the PollSets mFiledescriptors into their mFdSet. + multi_handle_w->mReadPollSet.refresh(); + refresh_t wres = multi_handle_w->mWritePollSet.refresh(); + // Add wake up fd if any, and pass NULL to select() if a set is empty. + fd_set* read_fd_set = multi_handle_w->mReadPollSet.access(); + FD_SET(mWakeUpFd, read_fd_set); + fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); + // Calculate nfds (ignored on windows). + int const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); + int const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); + int nfds = std::max(max_rfd, max_wfd) + 1; + llassert(0 <= nfds && nfds <= FD_SETSIZE); + llassert((max_rfd == -1) == (read_fd_set == NULL) && + (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. + llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && + (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); + int ready = 0; struct timeval timeout; long timeout_ms = multi_handle_w->getTimeOut(); // If no timeout is set, sleep 1 second. @@ -695,37 +748,41 @@ void AICurlThread::run(void) if (ready == -1) last_errno = errno; #endif - } - // Select returns the total number of bits set in each of the fd_set's (upon return), - // or -1 when an error occurred. A value of 0 means that a timeout occurred. - if (ready == -1) - { - llwarns << "select() failed: " << errno << ", " << strerror(errno) << llendl; - continue; - } - else if (ready == 0) - { - multi_handle_w->socket_action(CURL_SOCKET_TIMEOUT, 0); - } - else - { - if (multi_handle_w->mReadPollSet.is_set(mWakeUpFd)) + // Select returns the total number of bits set in each of the fd_set's (upon return), + // or -1 when an error occurred. A value of 0 means that a timeout occurred. + if (ready == -1) { - wakeup(multi_handle_w); - --ready; + llwarns << "select() failed: " << errno << ", " << strerror(errno) << llendl; + continue; } - MergeIterator iter(multi_handle_w->mReadPollSet, multi_handle_w->mWritePollSet); - int fd, ev_bitmask; - while (ready > 0 && iter.next(fd, ev_bitmask)) + else if (ready == 0) { - ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; - multi_handle_w->socket_action(fd, ev_bitmask); - llassert(ready >= 0); + multi_handle_w->socket_action(CURL_SOCKET_TIMEOUT, 0); } - llassert(ready == 0); + else + { + if (multi_handle_w->mReadPollSet.is_set(mWakeUpFd)) + { + // Process commands from main-thread. This can add or remove filedescriptors from the poll sets. + wakeup(multi_handle_w); + --ready; + } + // Handle all active filedescriptors. + MergeIterator iter(multi_handle_w->mReadPollSet, multi_handle_w->mWritePollSet); + int fd, ev_bitmask; + while (ready > 0 && iter.next(fd, ev_bitmask)) + { + ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; + multi_handle_w->socket_action(fd, ev_bitmask); + llassert(ready >= 0); + } + // Should have handled them all. + llassert(ready == 0); + } + multi_handle_w->check_run_count(); } - multi_handle_w->check_run_count(); } + AICurlMultiHandle::destroyInstance(); } //----------------------------------------------------------------------------- @@ -733,10 +790,10 @@ void AICurlThread::run(void) MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandles(0), mRunningHandles(0), mTimeOut(-1) { - curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETFUNCTION, &MultiHandle::socket_callback); - curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETDATA, this); - curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERFUNCTION, &MultiHandle::timer_callback); - curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERDATA, this); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETFUNCTION, &MultiHandle::socket_callback)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETDATA, this)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERFUNCTION, &MultiHandle::timer_callback)); + check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERDATA, this)); } MultiHandle::~MultiHandle() @@ -818,7 +875,13 @@ CURLMcode MultiHandle::assign(curl_socket_t sockfd, void* sockptr) CURLMsg const* MultiHandle::info_read(int* msgs_in_queue) const { - return curl_multi_info_read(mMultiHandle, msgs_in_queue); + CURLMsg const* ret = curl_multi_info_read(mMultiHandle, msgs_in_queue); + // NULL could be an error, but normally it isn't, so don't print anything and + // never increment Stats::multi_errors. However, lets just increment multi_calls + // when it certainly wasn't an error... + if (ret) + Stats::multi_calls++; + return ret; } CURLMcode MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) @@ -863,7 +926,8 @@ void MultiHandle::check_run_count(void) { CURL* easy = msg->easy_handle; ThreadSafeCurlEasyRequest* ptr; - curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr); + CURLcode rese = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &ptr); + llassert_always(rese == CURLE_OK); AICurlEasyRequest easy_request(ptr); llassert(*AICurlEasyRequest_wat(*easy_request) == easy); // Store the result and transfer info in the easy handle. @@ -904,6 +968,15 @@ void MultiHandle::check_run_count(void) } // namespace curlthread } // namespace AICurlPrivate +//static +void AICurlMultiHandle::destroyInstance(void) +{ + LLThreadLocalData& tldata = LLThreadLocalData::tldata(); + Dout(dc::curl, "Destroying AICurlMultiHandle [" << (void*)tldata.mCurlMultiHandle << "] for thread \"" << tldata.mName << "\"."); + delete tldata.mCurlMultiHandle; + tldata.mCurlMultiHandle = NULL; +} + //============================================================================= // MAIN-THREAD (needing to access the above declarations). @@ -913,8 +986,8 @@ AICurlMultiHandle& AICurlMultiHandle::getInstance(void) LLThreadLocalData& tldata = LLThreadLocalData::tldata(); if (!tldata.mCurlMultiHandle) { - llinfos << "Creating AICurlMultiHandle for thread \"" << tldata.mName << "\"." << llendl; tldata.mCurlMultiHandle = new AICurlMultiHandle; + Dout(dc::curl, "Created AICurlMultiHandle [" << (void*)tldata.mCurlMultiHandle << "] for thread \"" << tldata.mName << "\"."); } return *static_cast(tldata.mCurlMultiHandle); } @@ -934,6 +1007,21 @@ void wakeUpCurlThread(void) AICurlThread::sInstance->wakeup_thread(); } +void stopCurlThread(void) +{ + using curlthread::AICurlThread; + if (AICurlThread::sInstance) + { + AICurlThread::sInstance->stop_thread(); + int count = 101; + while(--count && !AICurlThread::sInstance->isStopped()) + { + ms_sleep(10); + } + Dout(dc::curl, "Curl thread" << (curlThreadIsRunning() ? " not" : "") << " stopped after " << ((100 - count) * 10) << "ms."); + } +} + } // namespace AICurlPrivate //----------------------------------------------------------------------------- @@ -994,7 +1082,7 @@ void AICurlEasyRequest::removeRequest(void) { // Write-lock the command queue. - command_queue_wat command_queue_w(command_queue); + command_queue_wat command_queue_w(command_queue); #ifdef SHOW_ASSERT // This debug code checks if we aren't calling removeRequest() twice for the same object. // That means that the thread calling this function already finished it, following from that diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index e8b080b94..a2262d9a8 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -92,7 +92,6 @@ class PollSet private: curl_socket_t* mFileDescriptors; - size_t mSize; // Size of mFileDescriptors array. int mNrFds; // The number of filedescriptors in the array. int mMaxFd; // The largest filedescriptor in the array, or -1 when it is empty. int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). @@ -172,6 +171,7 @@ class MultiHandle : public CurlMultiHandle class AICurlMultiHandle : public AIThreadSafeSingleThreadDC, public LLThreadLocalDataMember { public: static AICurlMultiHandle& getInstance(void); + static void destroyInstance(void); private: // Use getInstance(). AICurlMultiHandle(void) { } diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 5abe2df2e..72ccfb936 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -278,6 +278,12 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) mCurlEasyRequestStateMachinePtr->run(boost::bind(&LLXMLRPCTransaction::Impl::curlEasyRequestCallback, this, _1)); setStatus(LLXMLRPCTransaction::StatusStarted); } + else + { + // This deletes the statemachine immediately. + mCurlEasyRequestStateMachinePtr->kill(); + mCurlEasyRequestStateMachinePtr = NULL; + } } LLXMLRPCTransaction::Impl::~Impl() @@ -311,13 +317,19 @@ void LLXMLRPCTransaction::Impl::curlEasyRequestCallback(bool success) { llassert(mStatus == LLXMLRPCTransaction::StatusStarted || mStatus == LLXMLRPCTransaction::StatusDownloading); + AICurlEasyRequestStateMachine* state_machine = mCurlEasyRequestStateMachinePtr; + // We're done with the statemachine, one way or another. + // Set mCurlEasyRequestStateMachinePtr to NULL so we won't call mCurlEasyRequestStateMachinePtr->running() in the destructor. + // Note that the state machine auto-cleaning: it will be deleted by the main-thread after this function returns. + mCurlEasyRequestStateMachinePtr = NULL; + if (!success) { setStatus(LLXMLRPCTransaction::StatusOtherError, "Statemachine failed"); return; } - AICurlEasyRequest_wat curlEasyRequest_w(*mCurlEasyRequestStateMachinePtr->mCurlEasyRequest); + AICurlEasyRequest_wat curlEasyRequest_w(*state_machine->mCurlEasyRequest); CURLcode result; curlEasyRequest_w->getResult(&result, &mTransferInfo); diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp index a308be093..752fe0827 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp @@ -138,6 +138,8 @@ void AICurlEasyRequestStateMachine::finish_impl(void) curl_easy_request_w->send_events_to(NULL); curl_easy_request_w->revokeCallbacks(); } + // Auto clean up. + kill(); } AICurlEasyRequestStateMachine::AICurlEasyRequestStateMachine(bool buffered) : mBuffered(buffered), mCurlEasyRequest(buffered) From 07e7eeedd116e8020492fc303e4a1e0bc1562c18 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 4 Jul 2012 07:32:24 +0200 Subject: [PATCH 010/123] Added some windows code. Iterating directly over the elements of fd_set::fd_array in windows is faster than using FD_ISSET. --- indra/llmessage/aicurlthread.cpp | 144 ++++++++++++++++++++++--------- indra/llmessage/aicurlthread.h | 26 +++--- 2 files changed, 117 insertions(+), 53 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 005e4c620..e7b62947d 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -136,7 +136,10 @@ static size_t const MAXSIZE = std::max(1024, FD_SETSIZE); // Create an empty PollSet. PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), - mNrFds(0), mMaxFd(-1), mNext(0), mMaxFdSet(-1) + mNrFds(0), mNext(0) +#if !LL_WINDOWS + , mMaxFd(-1), mMaxFdSet(-1) +#endif { FD_ZERO(&mFdSet); } @@ -146,7 +149,9 @@ void PollSet::add(curl_socket_t s) { llassert_always(mNrFds < MAXSIZE); mFileDescriptors[mNrFds++] = s; +#if !LL_WINDOWS mMaxFd = std::max(mMaxFd, s); +#endif } // Remove filedescriptor s from the PollSet. @@ -180,54 +185,82 @@ void PollSet::remove(curl_socket_t s) // v // index: 0 1 2 3 4 5 // a b c s d e - int prev = mFileDescriptors[i]; // prev = 'e' - int max = -1; - for (--i; i >= 0 && prev != s; --i) + curl_socket_t cur = mFileDescriptors[i]; // cur = 'e' +#if !LL_WINDOWS + curl_socket_t max = -1; +#endif + while (cur != s) { - int cur = mFileDescriptors[i]; // cur = 'd' - mFileDescriptors[i] = prev; // Overwrite 'd' with 'e'. - max = std::max(max, prev); // max is the maximum value in 'i' or higher. - prev = cur; // prev = 'd' + llassert(i > 0); + curl_socket_t next = mFileDescriptors[--i]; // next = 'd' + mFileDescriptors[i] = cur; // Overwrite 'd' with 'e'. +#if !LL_WINDOWS + max = std::max(max, cur); // max is the maximum value in 'i' or higher. +#endif + cur = next; // cur = 'd' // i NrFds = 5 // v v // index: 0 1 2 3 4 - // a b c s e // prev = 'd' + // a b c s e // cur = 'd' // - // Next loop iteration: cur = 's', overwrite 's' with 'd', prev = 's'; loop terminates. + // Next loop iteration: next = 's', overwrite 's' with 'd', cur = 's'; loop terminates. // i NrFds = 5 // v v // index: 0 1 2 3 4 - // a b c d e // prev = 's' + // a b c d e // cur = 's' } - llassert(prev == s); + llassert(cur == s); // At this point i was decremented once more and points to the element before the old s. // i NrFds = 5 // v v // index: 0 1 2 3 4 - // a b c d e // max = std::max('d', 'e') + // a b c d e // max = std::max('d', 'e') // If mNext pointed to an element before s, it should be left alone. Otherwise, if mNext pointed // to s it must now point to 'd', or if it pointed beyond 's' it must be decremented by 1. - if (mNext > i + 1) // i + 1 is where s was. + if (mNext > i) // i is where s was. --mNext; +#if !LL_WINDOWS // If s was the largest file descriptor, we have to update mMaxFd. if (s == mMaxFd) { - while (i >= 0) + while (i > 0) { - int cur = mFileDescriptors[i]; - max = std::max(max, cur); - --i; + curl_socket_t next = mFileDescriptors[--i]; + max = std::max(max, next); } mMaxFd = max; llassert(mMaxFd < s); llassert((mMaxFd == -1) == (mNrFds == 0)); } +#endif // ALSO make sure that s is no longer set in mFdSet, or we might confuse libcurl by // calling curl_multi_socket_action for a socket that it told us to remove. +#if !LL_WINDOWS clr(s); +#else + // We have to use a custom implementation here, because we don't want to invalidate mIter. + // This is the same algorithm as above, but with mFdSet.fd_count instead of mNrFds, + // mFdSet.fd_array instead of mFileDescriptors and mIter instead of mNext. + if (FD_ISSET(s, &mFdSet)) + { + int i = --mFdSet.fd_count; + llassert(i >= 0); + curl_socket_t cur = mFdSet.fd_array[i]; + while (cur != s) + { + llassert(i > 0); + curl_socket_t next = mFileDescriptors[--i]; + mFileDescriptors[i] = cur; + cur = next; + } + if (mIter > i) + --mIter; + llassert(mIter <= mFdSet.fd_count); + } +#endif } bool PollSet::contains(curl_socket_t fd) const @@ -254,11 +287,15 @@ inline void PollSet::clr(curl_socket_t fd) refresh_t PollSet::refresh(void) { FD_ZERO(&mFdSet); +#if !LL_WINDOWS mCopiedFileDescriptors.clear(); +#endif if (mNrFds == 0) { +#if !LL_WINDOWS mMaxFdSet = -1; +#endif return empty_and_complete; } @@ -271,16 +308,20 @@ refresh_t PollSet::refresh(void) if (mNrFds >= FD_SETSIZE) { llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; +#if !LL_WINDOWS // Calculate mMaxFdSet. // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. int max = -1, i = mNext, count = 0; while (++count < FD_SETSIZE) { max = std::max(max, mFileDescriptors[i]); if (++i == mNrFds) i = 0; } mMaxFdSet = max; +#endif } else { mNext = 0; // Start at the beginning if we copy everything anyway. +#if !LL_WINDOWS mMaxFdSet = mMaxFd; +#endif } int count = 0; int i = mNext; @@ -292,7 +333,9 @@ refresh_t PollSet::refresh(void) return not_complete_not_empty; } FD_SET(mFileDescriptors[i], &mFdSet); +#if !LL_WINDOWS mCopiedFileDescriptors.push_back(mFileDescriptors[i]); +#endif if (++i == mNrFds) { // If we reached the end and start at the beginning, then we copied everything. @@ -306,9 +349,6 @@ refresh_t PollSet::refresh(void) return complete_not_empty; } -// FIXME: This needs a rewrite on Windows, as FD_ISSET is slow there; it would make -// more sense to iterate directly over the fd's in mFdSet on Windows. -// // The API reset(), get() and next() allows one to run over all filedescriptors // in mFdSet that are set. This works by running only over the filedescriptors // that were set initially (by the call to refresh()) and then checking if that @@ -324,13 +364,16 @@ refresh_t PollSet::refresh(void) // refresh(); // /* reset some or all bits in mFdSet */ // reset(); -// while (get() != -1) // next(); +// while (get() != CURL_SOCKET_BAD) // next(); // // Note also that this API is only used by MergeIterator, which wraps it // and provides a different API to use. void PollSet::reset(void) { +#if LL_WINDOWS + mIter = 0; +#else if (mCopiedFileDescriptors.empty()) mIter = mCopiedFileDescriptors.end(); else @@ -339,17 +382,27 @@ void PollSet::reset(void) if (!FD_ISSET(*mIter, &mFdSet)) next(); } +#endif } -inline int PollSet::get(void) const +inline curl_socket_t PollSet::get(void) const { - return (mIter == mCopiedFileDescriptors.end()) ? -1 : *mIter; +#if LL_WINDOWS + return (mIter >= mFdSet.fd_count) ? CURL_SOCKET_BAD : mFdSet.fd_array[mIter]; +#else + return (mIter == mCopiedFileDescriptors.end()) ? CURL_SOCKET_BAD : *mIter; +#endif } void PollSet::next(void) { +#if LL_WINDOWS + llassert(mIter < mFdSet.fd_count); + ++mIter; +#else llassert(mIter != mCopiedFileDescriptors.end()); // Only call next() if the last call to get() didn't return -1. while (++mIter != mCopiedFileDescriptors.end() && !FD_ISSET(*mIter, &mFdSet)); +#endif } //----------------------------------------------------------------------------- @@ -364,7 +417,7 @@ class MergeIterator public: MergeIterator(PollSet& readPollSet, PollSet& writePollSet); - bool next(int& fd_out, int& ev_bitmask_out); + bool next(curl_socket_t& fd_out, int& ev_bitmask_out); private: PollSet& mReadPollSet; @@ -380,12 +433,12 @@ MergeIterator::MergeIterator(PollSet& readPollSet, PollSet& writePollSet) : mWritePollSet.reset(); } -bool MergeIterator::next(int& fd_out, int& ev_bitmask_out) +bool MergeIterator::next(curl_socket_t& fd_out, int& ev_bitmask_out) { - int rfd = mReadPollSet.get(); - int wfd = mWritePollSet.get(); + curl_socket_t rfd = mReadPollSet.get(); + curl_socket_t wfd = mWritePollSet.get(); - if (rfd == -1 && wfd == -1) + if (rfd == CURL_SOCKET_BAD && wfd == CURL_SOCKET_BAD) return false; if (rfd == wfd) @@ -394,12 +447,12 @@ bool MergeIterator::next(int& fd_out, int& ev_bitmask_out) ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; mReadPollSet.next(); } - else if ((unsigned int)rfd < (unsigned int)wfd) // Use and increment smaller one, unless it's -1. + else if (wfd == CURL_SOCKET_BAD || rfd < wfd) // Use and increment smaller one, unless it's CURL_SOCKET_BAD. { fd_out = rfd; ev_bitmask_out = CURL_CSELECT_IN; mReadPollSet.next(); - if (wfd != -1 && mWritePollSet.is_set(rfd)) + if (wfd != CURL_SOCKET_BAD && mWritePollSet.is_set(rfd)) { ev_bitmask_out |= CURL_CSELECT_OUT; mWritePollSet.clr(rfd); @@ -410,7 +463,7 @@ bool MergeIterator::next(int& fd_out, int& ev_bitmask_out) fd_out = wfd; ev_bitmask_out = CURL_CSELECT_OUT; mWritePollSet.next(); - if (rfd != -1 && mReadPollSet.is_set(wfd)) + if (rfd != CURL_SOCKET_BAD && mReadPollSet.is_set(wfd)) { ev_bitmask_out |= CURL_CSELECT_IN; mReadPollSet.clr(wfd); @@ -501,8 +554,8 @@ class AICurlThread : public LLThread void create_wakeup_fds(void); void cleanup_wakeup_fds(void); - int mWakeUpFd_in; - int mWakeUpFd; + curl_socket_t mWakeUpFd_in; + curl_socket_t mWakeUpFd; int mZeroTimeOut; @@ -513,7 +566,7 @@ class AICurlThread : public LLThread AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD -AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(-1), mWakeUpFd(-1), mZeroTimeOut(0), mRunning(true) +AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(CURL_SOCKET_BAD), mWakeUpFd(CURL_SOCKET_BAD), mZeroTimeOut(0), mRunning(true) { create_wakeup_fds(); sInstance = this; @@ -554,9 +607,9 @@ void AICurlThread::create_wakeup_fds(void) // MAIN-THREAD void AICurlThread::cleanup_wakeup_fds(void) { - if (mWakeUpFd_in != -1) + if (mWakeUpFd_in != CURL_SOCKET_BAD) close(mWakeUpFd_in); - if (mWakeUpFd != -1) + if (mWakeUpFd != CURL_SOCKET_BAD) close(mWakeUpFd); } @@ -620,7 +673,7 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) { llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; close(mWakeUpFd); - mWakeUpFd = -1; + mWakeUpFd = CURL_SOCKET_BAD; mRunning = false; return; } @@ -668,8 +721,8 @@ void AICurlThread::run(void) AICurlMultiHandle_wat multi_handle_w(AICurlMultiHandle::getInstance()); while(mRunning) { - // If mRunning is true then we can only get here if mWakeUpFd != -1. - llassert(mWakeUpFd != -1); + // If mRunning is true then we can only get here if mWakeUpFd != CURL_SOCKET_BAD. + llassert(mWakeUpFd != CURL_SOCKET_BAD); // Copy the next batch of file descriptors from the PollSets mFiledescriptors into their mFdSet. multi_handle_w->mReadPollSet.refresh(); refresh_t wres = multi_handle_w->mWritePollSet.refresh(); @@ -678,14 +731,18 @@ void AICurlThread::run(void) FD_SET(mWakeUpFd, read_fd_set); fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); // Calculate nfds (ignored on windows). - int const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); - int const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); +#if !LL_WINDOWS + curl_socket_t const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); + curl_socket_t const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); int nfds = std::max(max_rfd, max_wfd) + 1; llassert(0 <= nfds && nfds <= FD_SETSIZE); llassert((max_rfd == -1) == (read_fd_set == NULL) && (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); +#else + int nfds = 64; +#endif int ready = 0; struct timeval timeout; long timeout_ms = multi_handle_w->getTimeOut(); @@ -769,7 +826,8 @@ void AICurlThread::run(void) } // Handle all active filedescriptors. MergeIterator iter(multi_handle_w->mReadPollSet, multi_handle_w->mWritePollSet); - int fd, ev_bitmask; + curl_socket_t fd; + int ev_bitmask; while (ready > 0 && iter.next(fd, ev_bitmask)) { ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index a2262d9a8..3980915d0 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -72,8 +72,10 @@ class PollSet // Return a pointer to the underlaying fd_set. fd_set* access(void) { return &mFdSet; } +#if !LL_WINDOWS // Return the largest fd set in mFdSet by refresh. - int get_max_fd(void) const { return mMaxFdSet; } + curl_socket_t get_max_fd(void) const { return mMaxFdSet; } +#endif // Return true if a filedescriptor is set in mFileDescriptors (used for debugging). bool contains(curl_socket_t s) const; @@ -85,22 +87,26 @@ class PollSet void clr(curl_socket_t fd); // Iterate over all file descriptors that were set by refresh and are still set in mFdSet. - void reset(void); // Reset the iterator. - int get(void) const; // Return next filedescriptor, or -1 when there are no more. - // Only valid if reset() was called after the last call to refresh(). - void next(void); // Advance to next filedescriptor. + void reset(void); // Reset the iterator. + curl_socket_t get(void) const; // Return next filedescriptor, or CURL_SOCKET_BAD when there are no more. + // Only valid if reset() was called after the last call to refresh(). + void next(void); // Advance to next filedescriptor. private: curl_socket_t* mFileDescriptors; - int mNrFds; // The number of filedescriptors in the array. - int mMaxFd; // The largest filedescriptor in the array, or -1 when it is empty. - int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). + int mNrFds; // The number of filedescriptors in the array. + int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). - fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). - int mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or -1 when it was empty. + fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). +#if !LL_WINDOWS + curl_socket_t mMaxFd; // The largest filedescriptor in the array, or CURL_SOCKET_BAD when it is empty. + curl_socket_t mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or CURL_SOCKET_BAD when it was empty. std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. +#else + int mIter; // Index into fd_set::fd_array. +#endif }; //----------------------------------------------------------------------------- From 14276b3cf875247be76180b044e333233cf824cb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 4 Jul 2012 08:44:22 +0200 Subject: [PATCH 011/123] Bug fix --- indra/llmessage/aicurlthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index e7b62947d..f91d49cdb 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -447,7 +447,7 @@ bool MergeIterator::next(curl_socket_t& fd_out, int& ev_bitmask_out) ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; mReadPollSet.next(); } - else if (wfd == CURL_SOCKET_BAD || rfd < wfd) // Use and increment smaller one, unless it's CURL_SOCKET_BAD. + else if (wfd == CURL_SOCKET_BAD || (rfd != CURL_SOCKET_BAD && rfd < wfd)) // Use and increment smaller one, unless it's CURL_SOCKET_BAD. { fd_out = rfd; ev_bitmask_out = CURL_CSELECT_IN; From d31c062693093151605dec43fcff27c2edb0550a Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Wed, 4 Jul 2012 12:44:05 +0200 Subject: [PATCH 012/123] Fix building with libcwd --- indra/llinventory/llsaleinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index 930901f30..88e7da7ac 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -30,8 +30,8 @@ * $/LicenseInfo$ */ -#include #include "linden_common.h" +#include #include "llsaleinfo.h" From ae9dadb5d8ab9aae36108c37e80873a36a006e81 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 4 Jul 2012 16:56:33 +0200 Subject: [PATCH 013/123] Improved layout of curl stats print out. --- indra/llmessage/aicurl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index a2ea765f1..0f9705154 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -508,12 +508,12 @@ LLAtomicU32 Stats::multi_errors; //static void Stats::print(void) { - llinfos << "====== CURL STATS ======" << llendl; - llinfos << " Curl multi errors/calls: " << std::dec << multi_errors << "/" << multi_calls << llendl; - llinfos << " Curl easy errors/calls: " << std::dec << easy_errors << "/" << easy_calls << llendl; - llinfos << " curl_easy_init() errors/calls: " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; + llinfos << "============ CURL STATS ============" << llendl; + llinfos << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl; + llinfos << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl; + llinfos << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; llinfos << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl; - llinfos << "=== END OF CURL STATS ===" << llendl; + llinfos << "========= END OF CURL STATS =========" << llendl; } // THREAD-SAFE From b8fc1f634e2de22b757338157fd38efcd7b69c63 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 5 Jul 2012 01:41:01 +0200 Subject: [PATCH 014/123] Print the curl library found during configure. Prints something like: -- Found CURL: /usr/lib/x86_64-linux-gnu/libcurl.so (found version "7.25.0") --- indra/cmake/CURL.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake index 3b35f0bae..29d789478 100644 --- a/indra/cmake/CURL.cmake +++ b/indra/cmake/CURL.cmake @@ -1,7 +1,7 @@ # -*- cmake -*- include(Prebuilt) -set(CURL_FIND_QUIETLY ON) +set(CURL_FIND_QUIETLY OFF) set(CURL_FIND_REQUIRED ON) if (STANDALONE) From df20f918bab16ad4e5b211cb041bf99f47986129 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 5 Jul 2012 03:10:16 +0200 Subject: [PATCH 015/123] New libcwd channel 'curlio'. Enable printing of libcurl 'IO' debug messages when libcwd channel 'curlio' is turned on (added to .libcwdrc). Avoiding a recompile. --- indra/cwdebug/debug.cc | 1 + indra/cwdebug/debug.h | 1 + indra/llmessage/aicurl.cpp | 6 ++---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index bd7d712f4..5235ee8d6 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -174,6 +174,7 @@ void stop_recording_backtraces(void) channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used for output related to class AIStateMachine. channel_ct caps DDCN("CAPS"); //!< This debug channel is used for output related to Capabilities. channel_ct curl DDCN("CURL"); //!< This debug channel is used for output related to Curl. + channel_ct curlio DDCN("CURLIO"); //!< This debug channel is used to print debug output of libcurl. } // namespace dc } // namespace DEBUGCHANNELS diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 10750f6ab..f148e7667 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -119,6 +119,7 @@ extern CWD_API channel_ct backtrace; extern CWD_API channel_ct statemachine; extern CWD_API channel_ct caps; extern CWD_API channel_ct curl; +extern CWD_API channel_ct curlio; #endif diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 0f9705154..ff3dba5f2 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -838,7 +838,7 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t marker << (void*)request->get_lockobj(); libcw_do.push_marker(); libcw_do.marker().assign(marker.str().data(), marker.str().size()); - LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curl|cond_nonewline_cf(infotype == CURLINFO_TEXT)) + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) switch (infotype) { case CURLINFO_TEXT: @@ -930,17 +930,15 @@ void CurlEasyRequest::applyDefaultOptions(void) //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); // Set the CURL options for either SOCKS or HTTP proxy. applyProxySettings(); -#if 0 // Cause libcurl to print all it's I/O traffic on the debug channel. Debug( - if (dc::curl.is_on()) + if (dc::curlio.is_on()) { setopt(CURLOPT_VERBOSE, 1); setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_callback); setopt(CURLOPT_DEBUGDATA, this); } ); -#endif } void CurlEasyRequest::finalizeRequest(std::string const& url) From f46d8e8a1062f5fc6aa047256560cf97b280bab7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 5 Jul 2012 23:30:47 +0200 Subject: [PATCH 016/123] Try to be smart about not printing garbage (binary) received from the server. --- indra/llmessage/aicurl.cpp | 44 +++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index ff3dba5f2..4044011dc 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -869,7 +869,49 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t LibcwDoutStream.write(buf, size); else if (infotype == CURLINFO_HEADER_IN || infotype == CURLINFO_HEADER_OUT) LibcwDoutStream << libcwd::buf2str(buf, size); - else if (infotype == CURLINFO_DATA_IN || infotype == CURLINFO_DATA_OUT) + else if (infotype == CURLINFO_DATA_IN) + { + LibcwDoutStream << size << " bytes"; + bool finished = false; + int i = 0; + while (i < size) + { + char c = buf[i]; + if (!('0' <= c && c <= '9') && !('a' <= c && c <= 'f')) + { + if (0 < i && i + 1 < size && buf[i] == '\r' && buf[i + 1] == '\n') + { + // Binary output: "[0-9a-f]*\r\n ...binary data..." + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, i + 2) << "\"..."; + finished = true; + } + break; + } + ++i; + } + if (!finished && size > 9 && buf[0] == '<') + { + // Human readable output: html, xml or llsd. + if (!strncmp(buf, "", 6)) + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"'; + finished = true; + } + } + if (!finished) + { + // Unknown format. Only print the first and last 20 characters. + if (size > 40UL) + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, 20) << "\"...\"" << libcwd::buf2str(&buf[size - 20], 20) << '"'; + } + else + { + LibcwDoutStream << ": \"" << libcwd::buf2str(buf, size) << '"'; + } + } + } + else if (infotype == CURLINFO_DATA_OUT) LibcwDoutStream << size << " bytes: \"" << libcwd::buf2str(buf, size) << '"'; else LibcwDoutStream << size << " bytes"; From 93778eccbb5e3b3ef7a6ec6882ea19e62fa3b44b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 7 Jul 2012 18:29:55 +0200 Subject: [PATCH 017/123] Compile fix for g++ 4.7 --- indra/llcommon/llstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index f9128b574..e1641c8cc 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -1401,7 +1401,7 @@ void LLStringUtilBase::testHarness() s2.erase( 4, 1 ); llassert( s2 == "hell"); - s2.insert( 0, 'y' ); + s2.insert( 0, std::string('y') ); llassert( s2 == "yhell"); s2.erase( 1, 3 ); llassert( s2 == "yl"); From 389074d1e995c2afc7e5f9b22e915ec98b4d7fa0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 7 Jul 2012 23:17:22 +0200 Subject: [PATCH 018/123] Fail at configure time when openGL is not found --- indra/plugins/base_media/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/plugins/base_media/CMakeLists.txt b/indra/plugins/base_media/CMakeLists.txt index 249a95bfc..f7917a794 100644 --- a/indra/plugins/base_media/CMakeLists.txt +++ b/indra/plugins/base_media/CMakeLists.txt @@ -2,6 +2,9 @@ project(media_plugin_base) +# Fail at configure, not link time. +set(OpenGL_FIND_REQUIRED ON) + include(00-Common) include(LLCommon) include(LLImage) From 744563a150406ca18d34dd915137cc7dc956488f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 7 Jul 2012 23:22:48 +0200 Subject: [PATCH 019/123] Use a newer libcurl version for linux. --- install.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install.xml b/install.xml index fd128ea38..d7b66421a 100644 --- a/install.xml +++ b/install.xml @@ -238,16 +238,16 @@ linux md5sum - 6244eec15fb26f9577785625fdb38f78 + 097d7571ce07626bbd248bbfd8f3e494 url - https://github.com/downloads/Shyotl/SingularityViewer/curl-7.20.1-linux-20100527.tar.bz2 + https://github.com/downloads/AlericInglewood/SingularityViewer/curl-7.21.1-linux-20110316.tar.bz2 linux64 md5sum - 8a28849f4b9d99601dbc8db9d6a2f9ba + d28fd5f4ed86b07d49443cb57e875cc1 url - https://github.com/downloads/Shyotl/SingularityViewer/curl-7.16.4a-linux64-20090303.tar.bz2 + https://github.com/downloads/AlericInglewood/SingularityViewer/curl-7.24.0-linux-x86_64-20120605.tar.bz2 windows From f012f664d2059a826f353375d220e9697cdd8654 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 9 Jul 2012 04:19:28 +0200 Subject: [PATCH 020/123] Threading voodoo: allow multiple concurrent calls to set_state(). This patch prepares AIStateMachine for the use of AITimer together with calls to set_state() from other threads. The extra problem in this case is that the main-thread CAN start running the state machine again (when the timer times out), while before it was assumed to be idle until a thread called set_state. This also takes into account that a thread might call set_state() and then AGAIN call set_state() before the main thread gets the chance to call idle() inbetween. --- indra/llcommon/llthread.cpp | 16 +-- indra/llcommon/llthread.h | 19 +-- .../aicurleasyrequeststatemachine.cpp | 20 +-- indra/newview/statemachine/aistatemachine.cpp | 117 ++++++++++++++++-- indra/newview/statemachine/aistatemachine.h | 28 ++++- 5 files changed, 160 insertions(+), 40 deletions(-) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index d1c02237f..43fe7596e 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -310,11 +310,13 @@ void LLThread::wakeLocked() } } -#ifdef SHOW_ASSERT -// This allows the use of llassert(is_main_thread()) to assure the current thread is the main thread. -static apr_os_thread_t main_thread_id; -LL_COMMON_API bool is_main_thread(void) { return apr_os_thread_equal(main_thread_id, apr_os_thread_current()); } -#endif +//static +apr_os_thread_t LLThread::sMainThreadID; + +void LLThread::set_main_thread_id(void) +{ + sMainThreadID = apr_os_thread_current(); +} // The thread private handle to access the LLThreadLocalData instance. apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey; @@ -346,10 +348,8 @@ void LLThreadLocalData::init(void) // Create the thread-local data for the main thread (this function is called by the main thread). LLThreadLocalData::create(NULL); -#ifdef SHOW_ASSERT // This function is called by the main thread. - main_thread_id = apr_os_thread_current(); -#endif + LLThread::set_main_thread_id(); } // This is called once for every thread when the thread is destructed. diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index b6287bb29..cea8c32c2 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -40,13 +40,6 @@ #include "llaprpool.h" #include "llatomic.h" -#ifdef SHOW_ASSERT -extern LL_COMMON_API bool is_main_thread(void); -#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0) -#else -#define ASSERT_SINGLE_THREAD do { } while(0) -#endif - class LLThread; class LLMutex; class LLCondition; @@ -89,6 +82,7 @@ private: class LL_COMMON_API LLThread { private: + static apr_os_thread_t sMainThreadID; static U32 sIDIter; static LLAtomicS32 sCount; static LLAtomicS32 sRunning; @@ -112,6 +106,7 @@ public: static S32 getCount() { return sCount; } static S32 getRunning() { return sRunning; } static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. + static bool is_main_thread(void) { return apr_os_thread_equal(LLThread::sMainThreadID, apr_os_thread_current()); } public: // PAUSE / RESUME functionality. See source code for important usage notes. @@ -135,6 +130,9 @@ public: // Return thread-local data for the current thread. static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); } + // Called once, from LLThreadLocalData::init(). + static void set_main_thread_id(void); + U32 getID() const { return mID; } private: @@ -178,6 +176,13 @@ protected: // mRunCondition->unlock(); }; +#ifdef SHOW_ASSERT +LL_COMMON_API inline bool is_main_thread(void) { return LLThread::is_main_thread(); } +#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0) +#else +#define ASSERT_SINGLE_THREAD do { } while(0) +#endif + //============================================================================ #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp index 752fe0827..1886c7165 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp @@ -34,7 +34,7 @@ enum curleasyrequeststatemachine_state_type { AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state, AICurlEasyRequestStateMachine_waitAdded, - AICurlEasyRequestStateMachine_waitFinished, + AICurlEasyRequestStateMachine_waitRemoved, AICurlEasyRequestStateMachine_finished }; @@ -44,7 +44,7 @@ char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) { AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest); AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded); - AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitFinished); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitRemoved); AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished); } return "UNKNOWN STATE"; @@ -63,7 +63,7 @@ void AICurlEasyRequestStateMachine::initialize_impl(void) // CURL-THREAD void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&) { - set_state(AICurlEasyRequestStateMachine_waitFinished); + set_state(AICurlEasyRequestStateMachine_waitRemoved); } // CURL-THREAD @@ -83,17 +83,17 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) { case AICurlEasyRequestStateMachine_addRequest: { - mCurlEasyRequest.addRequest(); set_state(AICurlEasyRequestStateMachine_waitAdded); - } - case AICurlEasyRequestStateMachine_waitAdded: - { - idle(); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. + idle(AICurlEasyRequestStateMachine_waitAdded); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. + // Only AFTER going idle, add request to curl thread; this is needed because calls to set_state() are + // ignored when the statemachine is not idle, and theoretically the callbacks could be called + // immediately after this call. + mCurlEasyRequest.addRequest(); break; } - case AICurlEasyRequestStateMachine_waitFinished: + case AICurlEasyRequestStateMachine_waitRemoved: { - idle(); // Wait till AICurlEasyRequestStateMachine::finished() is called. + idle(AICurlEasyRequestStateMachine_waitRemoved); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. break; } case AICurlEasyRequestStateMachine_finished: diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index badcdf5f5..faf9a75f1 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -152,37 +152,74 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot) void AIStateMachine::idle(void) { DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); + llassert(is_main_thread()); llassert(!mIdle); mIdle = true; mSleep = 0; +#ifdef SHOW_ASSERT + mCalledThreadUnsafeIdle = true; +#endif +} + +void AIStateMachine::idle(state_type current_run_state) +{ + DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); + llassert(is_main_thread()); + llassert(!mIdle); + mSetStateLock.lock(); + // Only go idle if the run state is (still) what we expect it to be. + // Otherwise assume that another thread called set_state() and continue running. + if (current_run_state == mRunState) + { + mIdle = true; + mSleep = 0; + } + mSetStateLock.unlock(); } // About thread safeness: // // The main thread initializes a statemachine and calls run, so a statemachine // runs in the main thread. However, it is allowed that a state calls idle() -// and then allows one and only one other thread to call cont() upon some +// and then allows one or more other threads to call cont() upon some // event (only once, of course, as idle() has to be called before cont() -// can be called again-- and another thread is not allowed to call idle()). -// Instead of cont(), the other thread may also call set_state(). - -void AIStateMachine::cont(void) +// can be called again-- and a non-main thread is not allowed to call idle()). +// Instead of cont() one may also call set_state(). +// Of course, this may give rise to a race condition; if that happens then +// the thread that calls cont() (set_state()) first is serviced, and the other +// thread(s) are ignored, as if they never called cont(). +void AIStateMachine::locked_cont(void) { DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); llassert(mIdle); // Atomic test mActive and change mIdle. mIdleActive.lock(); +#ifdef SHOW_ASSERT + mContThread = apr_os_thread_current(); +#endif mIdle = false; bool not_active = mActive == as_idle; mIdleActive.unlock(); + // mActive is only changed in AIStateMachine::mainloop, by the main-thread, and + // here, possibly by any thread. However, after setting mIdle to false above, it + // is impossible for any thread to come here, until after the main-thread called + // idle(). So, if this is the main thread then that certainly isn't going to + // happen until we left this function, while if this is another thread and the + // state machine is already running in the main thread then not_active is false + // and we're already at the end of this function. + // If not_active is true then main-thread is not running this statemachine. + // It might call cont() (or set_state()) but never locked_cont(), and will never + // start actually running until we are done here and release the lock on + // continued_statemachines_and_calling_mainloop again. It is therefore safe + // to release mSetStateLock here, with as advantage that if we're not the main- + // thread and not_active is true, then the main-thread won't block when it has + // a timer running that times out and calls set_state(). + mSetStateLock.unlock(); if (not_active) { AIWriteAccess cscm_w(continued_statemachines_and_calling_mainloop); - // We only get here when the statemachine was idle (set by the main thread), - // see first assertion. Hence, the main thread is not changing this, as the - // statemachine is not running. Thus, mActive can have changed when a THIRD - // thread called cont(), which is not allowed: if two threads can call cont() - // at any moment then the first assertion can't hold. + // See above: it is not possible that mActive was changed since not_active + // was set to true above. llassert_always(mActive == as_idle); cscm_w->continued_statemachines.push_back(this); if (!cscm_w->calling_mainloop) @@ -192,21 +229,77 @@ void AIStateMachine::cont(void) gIdleCallbacks.addFunction(&AIStateMachine::mainloop); } mActive = as_queued; - llassert_always(!mIdle); // It should never happen that one thread calls cont() while another calls idle() concurrently. + llassert_always(!mIdle); // It should never happen that the main thread calls idle(), while another thread calls cont() concurrently. } } void AIStateMachine::set_state(state_type state) { DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]"); + // Do not call abort(), finish() or kill() before cancelling the possibility that other threads call set_state(). + // Do not call set_state() unless running. llassert(mState == bs_run); + // Do not call idle() when set_state is called from another thread. + llassert(!mCalledThreadUnsafeIdle || LLThread::is_main_thread()); + + // If this function is called from another thread than the main thread, then we have to ignore + // it if we're not idle and the state is less than the current state. The main thread must + // be able to change the state to anything (also smaller values). Note that that only can work + // if the main thread itself at all times cancels thread callbacks that call set_state() + // before calling idle() again! + // + // Thus: main thead calls idle(), and tells one or more threads to do callbacks on events, + // which (might) call set_state(). If the main thread calls set_state first (currently only + // possible as a result of the use of a timer) it will set mIdle to false (here) then cancel + // the call backs from the other threads and only then call idle() again. + // Thus if you want other threads get here while mIdle is false to be ignored then the + // main thread should use a large value for the new run state. + // + // If a non-main thread calls set_state first, then the state is changed but the main thread + // can still override it if it calls set_state before handling the new state; in the latter + // case it would still be as if the call from the non-main thread was ignored. + // + // Concurrent calls from non-main threads however, always result in the largest state + // to prevail. + + // Stop race condition of multiple threads calling cont() or set_state() here. + mSetStateLock.lock(); + + // If the state machine is already running, and we are not the main-thread and the new + // state is less than the current state, ignore it. + if (!mIdle && !LLThread::is_main_thread() && state <= mRunState) + { +#ifdef SHOW_ASSERT + // It's a bit weird if the same thread does two calls on a row where the second call + // has a smaller value: warn about that. + if (mContThread == apr_os_thread_current()) + { + llwarns << "Ignoring call to set_state(" << state_str(state) << + ") by non-main thread before main-thread could react on previous call, " + "because new state is smaller than old state (" << state_str(mRunState) << ")." << llendl; + } +#endif + mSetStateLock.unlock(); + return; // Ignore. + } + + // Change mRunState to the requested value. if (mRunState != state) { mRunState = state; Dout(dc::statemachine, "mRunState set to " << state_str(mRunState)); } + + // Continue the state machine if appropriate. if (mIdle) - cont(); + locked_cont(); // This unlocks mSetStateLock. + else + mSetStateLock.unlock(); + + // If we get here then mIdle is false, so only mRunState can still be changed but we won't + // call locked_cont() anymore. When the main thread finally picks up on the state change, + // it will cancel any possible callbacks from other threads and process the largest state + // that this function was called with in the meantime. } void AIStateMachine::abort(void) diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 16eb28cd4..0fc71c2f4 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -207,6 +207,11 @@ class AIStateMachine { active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list. S64 mSleep; //!< Non-zero while the state machine is sleeping. LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive. + LLMutex mSetStateLock; //!< For critical areas in set_state() and locked_cont(). +#ifdef SHOW_ASSERT + apr_os_thread_t mContThread; //!< Thread that last called locked_cont(). + bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called. +#endif // Callback facilities. // From within an other state machine: @@ -233,16 +238,23 @@ class AIStateMachine { public: //! Create a non-running state machine. - AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); } + AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0), mParent(NULL), mCallback(NULL) +#ifdef SHOW_ASSERT + , mContThread(0), mCalledThreadUnsafeIdle(false) +#endif + { updateSettings(); } protected: //! The user should call 'kill()', not delete a AIStateMachine (derived) directly. virtual ~AIStateMachine() { llassert((mState == bs_killed && mActive == as_idle) || mState == bs_initialize); } public: - //! Halt the state machine until cont() is called. + //! Halt the state machine until cont() is called (not thread-safe). void idle(void); + //! Halt the state machine until cont() is called, provided it is still in 'cur_run_state'. + void idle(state_type current_run_state); + //! Temporarily halt the state machine. void yield_frame(unsigned int frames) { mSleep = -(S64)frames; } @@ -250,8 +262,18 @@ class AIStateMachine { void yield_ms(unsigned int ms) { mSleep = LLFastTimer::getCPUClockCount64() + LLFastTimer::countsPerSecond() * ms / 1000; } //! Continue running after calling idle. - void cont(void); + void cont(void) + { + mSetStateLock.lock(); + // Ignore calls to cont() if the statemachine isn't idle. See comments in set_state(). + // Calling cont() twice or after calling set_state(), without first calling idle(), is an error. + if (!mIdle) { llassert(mContThread != apr_os_thread_current()); mSetStateLock.unlock(); return; } + locked_cont(); + } + private: + void locked_cont(void); + public: //--------------------------------------- // Changing the state. From 0419f8bee948d70a6b0e9b8c8a9ed7e6f73e6828 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 10 Jul 2012 05:09:08 +0200 Subject: [PATCH 021/123] Add an AITimer to AICurlEasyRequestStateMachine. Fixes AIStateMachine to work thread-safe with the timer. --- indra/llmessage/aicurlthread.cpp | 6 +- indra/newview/llxmlrpctransaction.cpp | 11 ++- .../aicurleasyrequeststatemachine.cpp | 44 ++++++--- .../aicurleasyrequeststatemachine.h | 2 + indra/newview/statemachine/aistatemachine.cpp | 90 ++++++++++++++----- indra/newview/statemachine/aistatemachine.h | 2 +- indra/newview/statemachine/aitimer.h | 1 - 7 files changed, 115 insertions(+), 41 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index f91d49cdb..e1a9ba70a 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -831,11 +831,13 @@ void AICurlThread::run(void) while (ready > 0 && iter.next(fd, ev_bitmask)) { ready -= (ev_bitmask == (CURL_CSELECT_IN|CURL_CSELECT_OUT)) ? 2 : 1; + // This can cause libcurl to do callbacks and remove filedescriptors, causing us to reset their bits in the poll sets. multi_handle_w->socket_action(fd, ev_bitmask); llassert(ready >= 0); } - // Should have handled them all. - llassert(ready == 0); + // Note that ready is not necessarily 0 here, because it's possible + // that libcurl removed file descriptors which we subsequently + // didn't handle. } multi_handle_w->check_run_count(); } diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 72ccfb936..aceb63e65 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -325,7 +325,16 @@ void LLXMLRPCTransaction::Impl::curlEasyRequestCallback(bool success) if (!success) { - setStatus(LLXMLRPCTransaction::StatusOtherError, "Statemachine failed"); + // AICurlEasyRequestStateMachine did abort. + // This currently only happens when libcurl didn't finish before the timer expired. + std::ostringstream msg; + F32 timeout_value = gSavedSettings.getF32("CurlRequestTimeOut"); + msg << "Connection to " << mURI << " timed out (" << timeout_value << " s)!"; + if (timeout_value < 40) + { + msg << "\nTry increasing CurlRequestTimeOut in Debug Settings."; + } + setStatus(LLXMLRPCTransaction::StatusOtherError, msg.str()); return; } diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp index 1886c7165..48bdb09c3 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp @@ -30,11 +30,13 @@ #include "linden_common.h" #include "aicurleasyrequeststatemachine.h" +#include "llcontrol.h" enum curleasyrequeststatemachine_state_type { AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state, AICurlEasyRequestStateMachine_waitAdded, AICurlEasyRequestStateMachine_waitRemoved, + AICurlEasyRequestStateMachine_timedOut, // Only _finished has a higher priority than _timedOut. AICurlEasyRequestStateMachine_finished }; @@ -45,6 +47,7 @@ char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest); AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded); AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitRemoved); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_timedOut); AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished); } return "UNKNOWN STATE"; @@ -89,6 +92,15 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) // ignored when the statemachine is not idle, and theoretically the callbacks could be called // immediately after this call. mCurlEasyRequest.addRequest(); + + // Set an inactivity timer. + // This shouldn't really be necessary, except in the case of a bug + // in libcurl; but lets be sure and set a timer for inactivity. + static LLCachedControl CurlRequestTimeOut("CurlRequestTimeOut", 40.f); + mTimer = new AIPersistentTimer; // Do not delete timer upon expiration. + mTimer->setInterval(CurlRequestTimeOut); + mTimer->run(this, AICurlEasyRequestStateMachine_timedOut); + break; } case AICurlEasyRequestStateMachine_waitRemoved: @@ -96,6 +108,13 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) idle(AICurlEasyRequestStateMachine_waitRemoved); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. break; } + case AICurlEasyRequestStateMachine_timedOut: + { + // Libcurl failed to end on error(?)... abort operation in order to free + // this curl easy handle and to notify the application that it didn't work. + abort(); + break; + } case AICurlEasyRequestStateMachine_finished: { if (mBuffered) @@ -112,16 +131,10 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) void AICurlEasyRequestStateMachine::abort_impl(void) { - Dout(dc::curl, "AICurlEasyRequestStateMachine::abort_impl called for = " << (void*)mCurlEasyRequest.get()); - // We must first revoke the events, or the curl thread might change mRunState still. + DoutEntering(dc::curl, "AICurlEasyRequestStateMachine::abort_impl() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); + // Revert call to addRequest() if that was already called (and the request wasn't removed already again). + if (AICurlEasyRequestStateMachine_waitAdded <= mRunState && mRunState < AICurlEasyRequestStateMachine_finished) { - AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest); - curl_easy_request_w->send_events_to(NULL); - curl_easy_request_w->revokeCallbacks(); - } - if (mRunState >= AICurlEasyRequestStateMachine_waitAdded && mRunState < AICurlEasyRequestStateMachine_finished) - { - // Revert call to addRequest(). // Note that it's safe to call this even if the curl thread already removed it, or will removes it // after we called this, before processing the remove command; only the curl thread calls // MultiHandle::remove_easy_request, which is a no-op when called twice for the same easy request. @@ -131,14 +144,19 @@ void AICurlEasyRequestStateMachine::abort_impl(void) void AICurlEasyRequestStateMachine::finish_impl(void) { - Dout(dc::curl, "AICurlEasyRequestStateMachine::finish_impl called for = " << (void*)mCurlEasyRequest.get()); - if (!aborted()) + DoutEntering(dc::curl, "AICurlEasyRequestStateMachine::finish_impl() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); + // Revoke callbacks. { - AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest); + AICurlEasyRequest_wat curl_easy_request_w(*mCurlEasyRequest); curl_easy_request_w->send_events_to(NULL); curl_easy_request_w->revokeCallbacks(); } - // Auto clean up. + // Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid. + // Stop the timer. + mTimer->abort(); + // And delete it here. + mTimer->kill(); + // Auto clean up ourselves. kill(); } diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.h b/indra/newview/statemachine/aicurleasyrequeststatemachine.h index 41c3c7256..4dcd3b72d 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.h +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.h @@ -32,6 +32,7 @@ #define AICURLEASYREQUEST_H #include "aistatemachine.h" +#include "aitimer.h" #include "aicurl.h" // A curl easy request state machine. @@ -58,6 +59,7 @@ class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHa private: bool mBuffered; // Argument used for construction of mCurlEasyRequest. + AITimer* mTimer; // Expiration timer. protected: // AICurlEasyRequest Events. diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index faf9a75f1..5e0a5a200 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -121,7 +121,9 @@ void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bo // Mark that run() has been called, in case we're being called from a callback function. mState = bs_initialize; - cont(); + // Set mIdle to false and add statemachine to continued_statemachines. + mSetStateLock.lock(); + locked_cont(); } void AIStateMachine::run(callback_type::signal_type::slot_type const& slot) @@ -146,7 +148,9 @@ void AIStateMachine::run(callback_type::signal_type::slot_type const& slot) // Mark that run() has been called, in case we're being called from a callback function. mState = bs_initialize; - cont(); + // Set mIdle to false and add statemachine to continued_statemachines. + mSetStateLock.lock(); + locked_cont(); } void AIStateMachine::idle(void) @@ -163,7 +167,7 @@ void AIStateMachine::idle(void) void AIStateMachine::idle(state_type current_run_state) { - DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); + DoutEntering(dc::statemachine, "AIStateMachine::idle(" << state_str(current_run_state) << ") [" << (void*)this << "]"); llassert(is_main_thread()); llassert(!mIdle); mSetStateLock.lock(); @@ -190,7 +194,7 @@ void AIStateMachine::idle(state_type current_run_state) // thread(s) are ignored, as if they never called cont(). void AIStateMachine::locked_cont(void) { - DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); + DoutEntering(dc::statemachine, "AIStateMachine::locked_cont() [" << (void*)this << "]"); llassert(mIdle); // Atomic test mActive and change mIdle. mIdleActive.lock(); @@ -221,6 +225,7 @@ void AIStateMachine::locked_cont(void) // See above: it is not possible that mActive was changed since not_active // was set to true above. llassert_always(mActive == as_idle); + Dout(dc::statemachine, "Adding " << (void*)this << " to continued_statemachines"); cscm_w->continued_statemachines.push_back(this); if (!cscm_w->calling_mainloop) { @@ -236,11 +241,12 @@ void AIStateMachine::locked_cont(void) void AIStateMachine::set_state(state_type state) { DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]"); - // Do not call abort(), finish() or kill() before cancelling the possibility that other threads call set_state(). + + // Stop race condition of multiple threads calling cont() or set_state() here. + mSetStateLock.lock(); + // Do not call set_state() unless running. - llassert(mState == bs_run); - // Do not call idle() when set_state is called from another thread. - llassert(!mCalledThreadUnsafeIdle || LLThread::is_main_thread()); + llassert(mState == bs_run || !LLThread::is_main_thread()); // If this function is called from another thread than the main thread, then we have to ignore // it if we're not idle and the state is less than the current state. The main thread must @@ -262,17 +268,16 @@ void AIStateMachine::set_state(state_type state) // Concurrent calls from non-main threads however, always result in the largest state // to prevail. - // Stop race condition of multiple threads calling cont() or set_state() here. - mSetStateLock.lock(); - // If the state machine is already running, and we are not the main-thread and the new // state is less than the current state, ignore it. - if (!mIdle && !LLThread::is_main_thread() && state <= mRunState) + // Also, if abort() or finish() was called, then we should just ignore it. + if (mState != bs_run || + (!mIdle && !LLThread::is_main_thread() && state <= mRunState)) { #ifdef SHOW_ASSERT // It's a bit weird if the same thread does two calls on a row where the second call // has a smaller value: warn about that. - if (mContThread == apr_os_thread_current()) + if (mState == bs_run && mContThread == apr_os_thread_current()) { llwarns << "Ignoring call to set_state(" << state_str(state) << ") by non-main thread before main-thread could react on previous call, " @@ -283,6 +288,9 @@ void AIStateMachine::set_state(state_type state) return; // Ignore. } + // Do not call idle() when set_state is called from another thread; use idle(state_type) instead. + llassert(!mCalledThreadUnsafeIdle || LLThread::is_main_thread()); + // Change mRunState to the requested value. if (mRunState != state) { @@ -305,23 +313,56 @@ void AIStateMachine::set_state(state_type state) void AIStateMachine::abort(void) { DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]"); - llassert(mState == bs_run); - mState = bs_abort; - abort_impl(); - mAborted = true; - finish(); + // It's possible that abort() is called before calling AIStateMachine::multiplex. + // In that case the statemachine wasn't initialized yet and we should just kill() it. + if (LL_UNLIKELY(mState == bs_initialize)) + { + // It's ok to use the thread-unsafe idle() here, because if the statemachine + // wasn't started yet, then other threads won't call set_state() on it. + if (!mIdle) + idle(); + // run() calls locked_cont() after which the top of the mainloop adds this + // state machine to active_statemachines. Therefore, if the following fails + // then either the same statemachine called run() immediately followed by abort(), + // which is not allowed; or there were two active statemachines running, + // the first created a new statemachine and called run() on it, and then + // the other (before reaching the top of the mainloop) called abort() on + // that freshly created statemachine. Obviously, this is highly unlikely, + // but if that is the case then here we bump the statemachine into + // continued_statemachines to prevent kill() to delete this statemachine: + // the caller of abort() does not expect that. + if (LL_UNLIKELY(mActive == as_idle)) + { + mSetStateLock.lock(); + locked_cont(); + idle(); + } + kill(); + } + else + { + llassert(mState == bs_run); + mSetStateLock.lock(); + mState = bs_abort; // Causes additional calls to set_state to be ignored. + mSetStateLock.unlock(); + abort_impl(); + mAborted = true; + finish(); + } } void AIStateMachine::finish(void) { DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]"); + mSetStateLock.lock(); llassert(mState == bs_run || mState == bs_abort); // It is possible that mIdle is true when abort or finish was called from // outside multiplex_impl. However, that only may be done by the main thread. llassert(!mIdle || is_main_thread()); if (!mIdle) - idle(); - mState = bs_finish; + idle(); // After calling this, we don't want other threads to call set_state() anymore. + mState = bs_finish; // Causes additional calls to set_state to be ignored. + mSetStateLock.unlock(); finish_impl(); // Did finish_impl call kill()? Then that is only the default. Remember it. bool default_delete = (mState == bs_killed); @@ -370,18 +411,21 @@ void AIStateMachine::finish(void) if (mState == bs_killed && mActive == as_idle) { // Bump the statemachine onto the active statemachine list, or else it won't be deleted. - cont(); + mSetStateLock.lock(); + locked_cont(); idle(); } } void AIStateMachine::kill(void) { + DoutEntering(dc::statemachine, "AIStateMachine::kill() [" << (void*)this << "]"); // Should only be called from finish() (or when not running (bs_initialize)). - llassert(mIdle && (mState == bs_callback || mState == bs_finish || mState == bs_initialize)); + // However, also allow multiple calls to kill() on a row (bs_killed) (which effectively don't do anything). + llassert(mIdle && (mState == bs_callback || mState == bs_finish || mState == bs_initialize || mState == bs_killed)); base_state_type prev_state = mState; mState = bs_killed; - if (prev_state == bs_initialize) + if (prev_state == bs_initialize && mActive == as_idle) { // We're not running (ie being deleted by a parent statemachine), delete it immediately. delete this; diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 0fc71c2f4..6901f1055 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -267,7 +267,7 @@ class AIStateMachine { mSetStateLock.lock(); // Ignore calls to cont() if the statemachine isn't idle. See comments in set_state(). // Calling cont() twice or after calling set_state(), without first calling idle(), is an error. - if (!mIdle) { llassert(mContThread != apr_os_thread_current()); mSetStateLock.unlock(); return; } + if (mState != bs_run || !mIdle) { llassert(mState != bs_run || mContThread != apr_os_thread_current()); mSetStateLock.unlock(); return; } locked_cont(); } private: diff --git a/indra/newview/statemachine/aitimer.h b/indra/newview/statemachine/aitimer.h index 06d3e2ef4..c10559429 100644 --- a/indra/newview/statemachine/aitimer.h +++ b/indra/newview/statemachine/aitimer.h @@ -70,7 +70,6 @@ class AITimer : public AIStateMachine { * @brief Set the interval after which the timer should expire. * * @param interval Amount of time in seconds before the timer will expire. - * @param True if the timer should be deleted after it expires; false means it will keep firing at regular intervals. * * Call abort() at any time to stop the timer (and delete the AITimer object). */ From 7416d2aaf1338e4a70814bab3ef3eedc25b30a7a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 11 Jul 2012 19:25:29 +0200 Subject: [PATCH 022/123] Timer, time out, and clean up improvements. --- indra/llcrashlogger/llcrashlogger.cpp | 3 + indra/llmessage/aicurl.cpp | 64 ++++++--- indra/llmessage/aicurl.h | 2 +- indra/llmessage/aicurlprivate.h | 9 +- indra/llmessage/aicurlthread.cpp | 16 ++- indra/llmessage/aicurlthread.h | 2 +- indra/newview/llappviewer.cpp | 2 +- indra/newview/llmeshrepository.cpp | 2 - indra/newview/llxmlrpctransaction.cpp | 9 +- .../aicurleasyrequeststatemachine.cpp | 127 ++++++++++++++---- .../aicurleasyrequeststatemachine.h | 4 + indra/newview/statemachine/aistatemachine.cpp | 77 ++++++++--- indra/newview/statemachine/aistatemachine.h | 15 ++- 13 files changed, 254 insertions(+), 78 deletions(-) diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index a8be44313..d6f8995d9 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -371,6 +371,9 @@ void LLCrashLogger::updateApplication(const std::string& message) bool LLCrashLogger::init() { + // Initialized curl + AICurlInterface::initCurl(); + // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 4044011dc..4636fae7c 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -80,6 +80,7 @@ enum gSSLlib_type { // No locking needed: initialized before threads are created, and subsequently only read. gSSLlib_type gSSLlib; bool gSetoptParamsNeedDup; +void (*statemachines_flush_hook)(void); } // namespace @@ -264,9 +265,9 @@ static unsigned int encoded_version(int major, int minor, int patch) namespace AICurlInterface { // MAIN-THREAD -void initCurl(F32 curl_request_timeout, S32 max_number_handles) +void initCurl(void (*flush_hook)()) { - DoutEntering(dc::curl, "AICurlInterface::initCurl(" << curl_request_timeout << ", " << max_number_handles << ")"); + DoutEntering(dc::curl, "AICurlInterface::initCurl(" << (void*)flush_hook << ")"); llassert(LLThread::getRunning() == 0); // We must not call curl_global_init unless we are the only thread. CURLcode res = curl_global_init(CURL_GLOBAL_ALL); @@ -340,17 +341,24 @@ void initCurl(F32 curl_request_timeout, S32 max_number_handles) } llassert_always(!gSetoptParamsNeedDup); // Might add support later. } + + // Called in cleanupCurl. + statemachines_flush_hook = flush_hook; } // MAIN-THREAD void cleanupCurl(void) { - using AICurlPrivate::stopCurlThread; - using AICurlPrivate::curlThreadIsRunning; + using namespace AICurlPrivate; DoutEntering(dc::curl, "AICurlInterface::cleanupCurl()"); stopCurlThread(); + if (CurlMultiHandle::getTotalMultiHandles() != 0) + llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl; + if (statemachines_flush_hook) + (*statemachines_flush_hook)(); + Stats::print(); ssl_cleanup(); llassert(LLThread::getRunning() <= (curlThreadIsRunning() ? 1 : 0)); // We must not call curl_global_cleanup unless we are the only thread left. @@ -547,6 +555,9 @@ void CurlEasyHandle::handle_easy_error(CURLcode code) // Throws AICurlNoEasyHandle. CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) +#ifdef SHOW_ASSERT + , mRemovedPerCommand(true) +#endif { mEasyHandle = curl_easy_init(); #if 0 @@ -566,6 +577,22 @@ CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NU } } +#if 0 // Not used +CurlEasyHandle::CurlEasyHandle(CurlEasyHandle const& orig) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) +#ifdef SHOW_ASSERT + , mRemovedPerCommand(true) +#endif +{ + mEasyHandle = curl_easy_duphandle(orig.mEasyHandle); + Stats::easy_init_calls++; + if (!mEasyHandle) + { + Stats::easy_init_errors++; + throw AICurlNoEasyHandle("curl_easy_duphandle() returned NULL"); + } +} +#endif + CurlEasyHandle::~CurlEasyHandle() { llassert(!mActiveMultiHandle); @@ -1075,24 +1102,25 @@ CurlResponderBuffer::~CurlResponderBuffer() curl_easy_request_w->revokeCallbacks(); if (mResponder) { - llwarns << "Calling ~CurlResponderBuffer() with active responder!" << llendl; - llassert(false); // Does this ever happen? And if so, what does it mean? - // FIXME: Does this really mean it timed out? - mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput); - mResponder = NULL; + // If the responder is still alive, then that means that CurlResponderBuffer::processOutput was + // never called, which means that the removed_from_multi_handle event never happened. + // This is definitely an internal error as it can only happen when libcurl is too slow, + // in which case AICurlEasyRequestStateMachine::mTimer times out, but that already + // calls CurlResponderBuffer::timed_out(). So, this really should never happen. + llerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl; + timed_out(); } } +void CurlResponderBuffer::timed_out(void) +{ + mResponder->completedRaw(HTTP_INTERNAL_ERROR, "Request timeout, aborted.", sChannels, mOutput); + mResponder = NULL; +} + void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w) { - if (mResponder) - { - llwarns << "Calling CurlResponderBuffer::resetState() for active easy handle!" << llendl; - llassert(false); // Does this ever happen? And if so, what does it mean? - // FIXME: Does this really mean it timed out? - mResponder->completedRaw(HTTP_REQUEST_TIME_OUT, "Request timeout, aborted.", sChannels, mOutput); - mResponder = NULL; - } + llassert(!mResponder); curl_easy_request_w->resetState(); @@ -1287,8 +1315,6 @@ CurlMultiHandle::~CurlMultiHandle() Stats::multi_calls++; int total = --sTotalMultiHandles; Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining."); - if (total == 0) - Stats::print(); } } // namespace AICurlPrivate diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index a0ab05408..a9a2a9d64 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -90,7 +90,7 @@ struct TransferInfo { // Called once at start of application (from newview/llappviewer.cpp by main thread (before threads are created)), // with main purpose to initialize curl. -void initCurl(F32 curl_request_timeout = 120.f, S32 max_number_handles = 256); +void initCurl(void (*)(void) = NULL); // Called once at start of application (from LLAppViewer::initThreads), starts AICurlThread. void startCurlThread(void); diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 82d4e9c44..892228bae 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -79,7 +79,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURLcode setopt(CURLoption option, BUILTIN parameter); // Clone a libcurl session handle using all the options previously set. - CurlEasyHandle(CurlEasyHandle const& orig) : mEasyHandle(curl_easy_duphandle(orig.mEasyHandle)), mActiveMultiHandle(NULL), mErrorBuffer(NULL) { } + //CurlEasyHandle(CurlEasyHandle const& orig); // URL encode/decode the given string. char* escape(char* url, int length); @@ -102,6 +102,10 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURL* mEasyHandle; CURLM* mActiveMultiHandle; char* mErrorBuffer; +#ifdef SHOW_ASSERT + public: + bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread. +#endif private: // This should only be called from MultiHandle; add/remove an easy handle to/from a multi handle. @@ -279,6 +283,9 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents { std::stringstream& getHeaderOutput() { return mHeaderOutput; } LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } + // Called if libcurl doesn't deliver within CurlRequestTimeOut seconds. + void timed_out(void); + // Called after removed_from_multi_handle was called. void processOutput(AICurlEasyRequest_wat& curl_easy_request_w); diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index e1a9ba70a..3c1ce5999 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -702,7 +702,7 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) multi_handle_w->add_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); break; case cmd_remove: - multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request())); + multi_handle_w->remove_easy_request(AICurlEasyRequest(command_being_processed_r->easy_request()), true); break; } // Done processing. @@ -858,6 +858,8 @@ MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandl MultiHandle::~MultiHandle() { + llinfos << "Destructing MultiHandle with " << mAddedEasyRequests.size() << " active curl easy handles." << llendl; + // This thread was terminated. // Curl demands that all handles are removed from the multi session handle before calling curl_multi_cleanup. for(addedEasyRequests_type::iterator iter = mAddedEasyRequests.begin(); iter != mAddedEasyRequests.end(); iter = mAddedEasyRequests.begin()) @@ -958,7 +960,7 @@ CURLMcode MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request) return ret; } -CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request) +CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command) { addedEasyRequests_type::iterator iter = mAddedEasyRequests.find(easy_request); if (iter == mAddedEasyRequests.end()) @@ -967,6 +969,9 @@ CURLMcode MultiHandle::remove_easy_request(AICurlEasyRequest const& easy_request { AICurlEasyRequest_wat curl_easy_request_w(**iter); res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); +#ifdef SHOW_ASSERT + curl_easy_request_w->mRemovedPerCommand = as_per_command; +#endif } mAddedEasyRequests.erase(iter); mHandleAddedOrRemoved = true; @@ -1173,8 +1178,11 @@ void AICurlEasyRequest::removeRequest(void) } else { - // May not already have been removed from multi session handle. - llassert(AICurlEasyRequest_wat(*get())->active()); + // May not already have been removed from multi session handle as per command from the main thread (through this function thus). + { + AICurlEasyRequest_wat curl_easy_request_w(*get()); + llassert(curl_easy_request_w->active() || !curl_easy_request_w->mRemovedPerCommand); + } } } #endif diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index 3980915d0..c0f9d03f7 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -123,7 +123,7 @@ class MultiHandle : public CurlMultiHandle // Add/remove an easy handle to/from a multi session. CURLMcode add_easy_request(AICurlEasyRequest const& easy_request); - CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request); + CURLMcode remove_easy_request(AICurlEasyRequest const& easy_request, bool as_per_command = false); // Reads/writes available data from a particular socket (non-blocking). CURLMcode socket_action(curl_socket_t sockfd, int ev_bitmask); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 41aba1942..6364c1388 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -612,7 +612,7 @@ bool LLAppViewer::init() initLogging(); // Curl must be initialized before any thread is running. - AICurlInterface::initCurl(); + AICurlInterface::initCurl(&AIStateMachine::flush); // Logging is initialized. Now it's safe to start the error thread. startErrorThread(); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index e949460c5..0b70eb02e 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1613,7 +1613,6 @@ void LLMeshUploadThread::doWholeModelUpload() LLSD body = full_model_data["asset_resources"]; dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); LLCurlRequest::headers_t headers; - //FIXME: this might throw AICurlNoEasyHandle mCurlRequest->post(mWholeModelUploadURL, headers, body, new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); do @@ -1645,7 +1644,6 @@ void LLMeshUploadThread::requestWholeModelFee() mPendingUploads++; LLCurlRequest::headers_t headers; - //FIXME: this might throw AICurlNoEasyHandle mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index aceb63e65..2282249df 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -479,12 +479,17 @@ void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code) size_t LLXMLRPCTransaction::Impl::curlDownloadCallback( char* data, size_t size, size_t nmemb, void* user_data) { - DoutEntering(dc::curl, "LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, size * nmemb) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); - Impl& impl(*(Impl*)user_data); size_t n = size * nmemb; +#ifdef CWDEBUG + if (n < 80) + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, n) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); + else + Dout(dc::curl, "Entering LLXMLRPCTransaction::Impl::curlDownloadCallback(\"" << buf2str(data, 40) << "\"...\"" << buf2str(data + n - 40, 40) << "\", " << size << ", " << nmemb << ", " << user_data << ")"); +#endif + impl.mResponseText.append(data, n); if (impl.mStatus == LLXMLRPCTransaction::StatusStarted) diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp index 48bdb09c3..0c2f0dbc5 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.cpp @@ -35,9 +35,11 @@ enum curleasyrequeststatemachine_state_type { AICurlEasyRequestStateMachine_addRequest = AIStateMachine::max_state, AICurlEasyRequestStateMachine_waitAdded, - AICurlEasyRequestStateMachine_waitRemoved, - AICurlEasyRequestStateMachine_timedOut, // Only _finished has a higher priority than _timedOut. - AICurlEasyRequestStateMachine_finished + AICurlEasyRequestStateMachine_added, + AICurlEasyRequestStateMachine_timedOut, // This must be smaller than the rest, so they always overrule. + AICurlEasyRequestStateMachine_finished, + AICurlEasyRequestStateMachine_removed, // The removed states must be largest two, so they are never ignored. + AICurlEasyRequestStateMachine_removed_after_finished }; char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) const @@ -46,9 +48,11 @@ char const* AICurlEasyRequestStateMachine::state_str_impl(state_type run_state) { AI_CASE_RETURN(AICurlEasyRequestStateMachine_addRequest); AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitAdded); - AI_CASE_RETURN(AICurlEasyRequestStateMachine_waitRemoved); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_added); AI_CASE_RETURN(AICurlEasyRequestStateMachine_timedOut); AI_CASE_RETURN(AICurlEasyRequestStateMachine_finished); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed); + AI_CASE_RETURN(AICurlEasyRequestStateMachine_removed_after_finished); } return "UNKNOWN STATE"; } @@ -60,38 +64,57 @@ void AICurlEasyRequestStateMachine::initialize_impl(void) llassert(curlEasyRequest_w->is_finalized()); // Call finalizeRequest(url) before calling run(). curlEasyRequest_w->send_events_to(this); } + mAdded = false; + mTimedOut = false; + mFinished = false; + mHandled = false; set_state(AICurlEasyRequestStateMachine_addRequest); } // CURL-THREAD void AICurlEasyRequestStateMachine::added_to_multi_handle(AICurlEasyRequest_wat&) { - set_state(AICurlEasyRequestStateMachine_waitRemoved); + set_state(AICurlEasyRequestStateMachine_added); } // CURL-THREAD void AICurlEasyRequestStateMachine::finished(AICurlEasyRequest_wat&) { + mFinished = true; + set_state(AICurlEasyRequestStateMachine_finished); } // CURL-THREAD void AICurlEasyRequestStateMachine::removed_from_multi_handle(AICurlEasyRequest_wat&) { - set_state(AICurlEasyRequestStateMachine_finished); + set_state(mFinished ? AICurlEasyRequestStateMachine_removed_after_finished : AICurlEasyRequestStateMachine_removed); } void AICurlEasyRequestStateMachine::multiplex_impl(void) { - switch (mRunState) + mSetStateLock.lock(); + state_type current_state = mRunState; + mSetStateLock.unlock(); + switch (current_state) { case AICurlEasyRequestStateMachine_addRequest: { set_state(AICurlEasyRequestStateMachine_waitAdded); - idle(AICurlEasyRequestStateMachine_waitAdded); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. + idle(AICurlEasyRequestStateMachine_waitAdded); // Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called. // Only AFTER going idle, add request to curl thread; this is needed because calls to set_state() are // ignored when the statemachine is not idle, and theoretically the callbacks could be called // immediately after this call. - mCurlEasyRequest.addRequest(); + mAdded = true; + mCurlEasyRequest.addRequest(); // This causes the state to be changed, now or later, to + // AICurlEasyRequestStateMachine_added, then + // AICurlEasyRequestStateMachine_finished and then + // AICurlEasyRequestStateMachine_removed_after_finished. + + // The first two states might be skipped thus, and the state at this point is one of + // 1) AICurlEasyRequestStateMachine_waitAdded (idle) + // 2) AICurlEasyRequestStateMachine_added (running) + // 3) AICurlEasyRequestStateMachine_finished (running) + // 4) AICurlEasyRequestStateMachine_removed_after_finished (running) // Set an inactivity timer. // This shouldn't really be necessary, except in the case of a bug @@ -99,31 +122,85 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) static LLCachedControl CurlRequestTimeOut("CurlRequestTimeOut", 40.f); mTimer = new AIPersistentTimer; // Do not delete timer upon expiration. mTimer->setInterval(CurlRequestTimeOut); - mTimer->run(this, AICurlEasyRequestStateMachine_timedOut); - + mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false); break; } - case AICurlEasyRequestStateMachine_waitRemoved: + case AICurlEasyRequestStateMachine_added: { - idle(AICurlEasyRequestStateMachine_waitRemoved); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. + // The request was added to the multi handle. This is a no-op, which is good cause + // this state might be skipped anyway ;). + idle(current_state); // Wait for the next event. + + // The state at this point is one of + // 1) AICurlEasyRequestStateMachine_added (idle) + // 2) AICurlEasyRequestStateMachine_finished (running) + // 3) AICurlEasyRequestStateMachine_removed_after_finished (running) break; } case AICurlEasyRequestStateMachine_timedOut: { - // Libcurl failed to end on error(?)... abort operation in order to free - // this curl easy handle and to notify the application that it didn't work. - abort(); + // It is possible that exactly at this point the state changes into + // AICurlEasyRequestStateMachine_finished, with as result that mTimedOut + // is set while we will continue with that state. Hence that mTimedOut + // is explicitly reset in that state. + + // Libcurl failed to deliver within a reasonable time... Abort operation in order + // to free this curl easy handle and to notify the application that it didn't work. + mTimedOut = true; + llassert(mAdded); + mAdded = false; + mCurlEasyRequest.removeRequest(); + idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. break; } case AICurlEasyRequestStateMachine_finished: + case AICurlEasyRequestStateMachine_removed_after_finished: { - if (mBuffered) + if (!mHandled) + { + // Only do this once. + mHandled = true; + + // Stop the timer. Note that it's the main thread that generates timer events, + // so we're certain that there will be no time out anymore if we reach this point. + mTimer->abort(); + + // The request finished and either data or an error code is available. + if (mBuffered) + { + AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest); + AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest); + buffered_easy_request_w->processOutput(easy_request_w); + } + } + + if (current_state == AICurlEasyRequestStateMachine_finished) + { + idle(current_state); // Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called. + break; + } + + // See above. + mTimedOut = false; + /* Fall-Through */ + } + case AICurlEasyRequestStateMachine_removed: + { + // The request was removed from the multi handle. + if (mBuffered && mTimedOut) { AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest); AICurlResponderBuffer_wat buffered_easy_request_w(*mCurlEasyRequest); - buffered_easy_request_w->processOutput(easy_request_w); + buffered_easy_request_w->timed_out(); } - finish(); + + // We're done. If we timed out, abort -- or else the application will + // think that getResult() will return a valid error code from libcurl. + if (mTimedOut) + abort(); + else + finish(); + break; } } @@ -132,12 +209,13 @@ void AICurlEasyRequestStateMachine::multiplex_impl(void) void AICurlEasyRequestStateMachine::abort_impl(void) { DoutEntering(dc::curl, "AICurlEasyRequestStateMachine::abort_impl() [" << (void*)this << "] [" << (void*)mCurlEasyRequest.get() << "]"); - // Revert call to addRequest() if that was already called (and the request wasn't removed already again). - if (AICurlEasyRequestStateMachine_waitAdded <= mRunState && mRunState < AICurlEasyRequestStateMachine_finished) + // Revert call to addRequest() if that was already called (and the request wasn't removed again already). + if (mAdded) { // Note that it's safe to call this even if the curl thread already removed it, or will removes it // after we called this, before processing the remove command; only the curl thread calls // MultiHandle::remove_easy_request, which is a no-op when called twice for the same easy request. + mAdded = false; mCurlEasyRequest.removeRequest(); } } @@ -152,10 +230,9 @@ void AICurlEasyRequestStateMachine::finish_impl(void) curl_easy_request_w->revokeCallbacks(); } // Note that even if the timer expired, it wasn't deleted because we used AIPersistentTimer; so mTimer is still valid. - // Stop the timer. - mTimer->abort(); - // And delete it here. - mTimer->kill(); + // Stop the timer, if it's still running. + if (!mHandled) + mTimer->abort(); // Auto clean up ourselves. kill(); } diff --git a/indra/newview/statemachine/aicurleasyrequeststatemachine.h b/indra/newview/statemachine/aicurleasyrequeststatemachine.h index 4dcd3b72d..e5f875138 100644 --- a/indra/newview/statemachine/aicurleasyrequeststatemachine.h +++ b/indra/newview/statemachine/aicurleasyrequeststatemachine.h @@ -59,6 +59,10 @@ class AICurlEasyRequestStateMachine : public AIStateMachine, public AICurlEasyHa private: bool mBuffered; // Argument used for construction of mCurlEasyRequest. + bool mAdded; // Set when the last command to the curl thread was to add the request. + bool mTimedOut; // Set if the expiration timer timed out. + bool mFinished; // Set by the curl thread to signal it finished. + bool mHandled; // Set when we processed the received data. AITimer* mTimer; // Expiration timer. protected: diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index 5e0a5a200..db181ce45 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -89,7 +89,7 @@ void AIStateMachine::updateSettings(void) // Public methods // -void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent) +void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent, bool on_abort_signal_parent) { DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") << ", " << abort_parent << ") [" << (void*)this << "]"); // Must be the first time we're being run, or we must be called from a callback function. @@ -111,6 +111,7 @@ void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bo mNewParentState = new_parent_state; mAbortParent = abort_parent; + mOnAbortSignalParent = on_abort_signal_parent; } // If abort_parent is requested then a parent must be provided. @@ -272,7 +273,7 @@ void AIStateMachine::set_state(state_type state) // state is less than the current state, ignore it. // Also, if abort() or finish() was called, then we should just ignore it. if (mState != bs_run || - (!mIdle && !LLThread::is_main_thread() && state <= mRunState)) + (!mIdle && state <= mRunState && !LLThread::is_main_thread())) { #ifdef SHOW_ASSERT // It's a bit weird if the same thread does two calls on a row where the second call @@ -379,7 +380,7 @@ void AIStateMachine::finish(void) mParent->abort(); mParent = NULL; } - else + else if (!mAborted || mOnAbortSignalParent) { mParent->set_state(mNewParentState); } @@ -491,25 +492,28 @@ void AIStateMachine::multiplex(U64 current_time) multiplex_impl(); } +//static +void AIStateMachine::add_continued_statemachines(void) +{ + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + bool nonempty = false; + for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter) + { + nonempty = true; + active_statemachines.push_back(QueueElement(*iter)); + Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); + (*iter)->mActive = as_active; + } + if (nonempty) + AIWriteAccess(cscm_r)->continued_statemachines.clear(); +} + static LLFastTimer::DeclareTimer FTM_STATEMACHINE("State Machine"); // static void AIStateMachine::mainloop(void*) { LLFastTimer t(FTM_STATEMACHINE); - // Add continued state machines. - { - AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); - bool nonempty = false; - for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter) - { - nonempty = true; - active_statemachines.push_back(QueueElement(*iter)); - Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); - (*iter)->mActive = as_active; - } - if (nonempty) - AIWriteAccess(cscm_r)->continued_statemachines.clear(); - } + add_continued_statemachines(); llassert(!active_statemachines.empty()); // Run one or more state machines. U64 total_clocks = 0; @@ -523,7 +527,7 @@ void AIStateMachine::mainloop(void*) // This might call idle() and then pass the statemachine to another thread who then may call cont(). // Hence, after this isn't not sure what mIdle is, and it can change from true to false at any moment, // if it is true after this function returns. - iter->statemachine().multiplex(start); + statemachine.multiplex(start); U64 delta = LLFastTimer::getCPUClockCount64() - start; iter->add(delta); total_clocks += delta; @@ -589,3 +593,40 @@ void AIStateMachine::mainloop(void*) } } } + +// static +void AIStateMachine::flush(void) +{ + DoutEntering(dc::curl, "AIStateMachine::flush(void)"); + add_continued_statemachines(); + // Abort all state machines. + for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) + { + AIStateMachine& statemachine(iter->statemachine()); + if (statemachine.running()) + statemachine.abort(); + } + for (int batch = 0;; ++batch) + { + // Run mainloop until all state machines are idle. + for(;;) + { + { + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + if (!cscm_r->calling_mainloop) + break; + } + mainloop(NULL); + } + if (batch == 1) + break; + add_continued_statemachines(); + // Kill all state machines. + for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) + { + AIStateMachine& statemachine(iter->statemachine()); + if (statemachine.running()) + statemachine.kill(); + } + } +} diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 6901f1055..650e7f296 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -207,7 +207,6 @@ class AIStateMachine { active_type mActive; //!< Whether statemachine is idle, queued to be added to the active list, or already on the active list. S64 mSleep; //!< Non-zero while the state machine is sleeping. LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive. - LLMutex mSetStateLock; //!< For critical areas in set_state() and locked_cont(). #ifdef SHOW_ASSERT apr_os_thread_t mContThread; //!< Thread that last called locked_cont(). bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called. @@ -218,6 +217,7 @@ class AIStateMachine { AIStateMachine* mParent; //!< The parent object that started this state machine, or NULL if there isn't any. state_type mNewParentState; //!< The state at which the parent should continue upon a successful finish. bool mAbortParent; //!< If true, abort parent on abort(). Otherwise continue as normal. + bool mOnAbortSignalParent; //!< If true and mAbortParent is false, change state of parent even on abort. // From outside a state machine: struct callback_type { typedef boost::signals2::signal signal_type; @@ -233,8 +233,10 @@ class AIStateMachine { static AIThreadSafeSimpleDC sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame. protected: + LLMutex mSetStateLock; //!< For critical areas in set_state() and locked_cont(). + //! State of the derived class. Only valid if mState == bs_run. Call set_state to change. - state_type mRunState; + volatile state_type mRunState; public: //! Create a non-running state machine. @@ -280,7 +282,7 @@ class AIStateMachine { //! Change state to bs_run. May only be called after creation or after returning from finish(). // If parent is non-NULL, change the parent state machine's state to new_parent_state // upon finish, or in the case of an abort and when abort_parent is true, call parent->abort() instead. - void run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent = true); + void run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent = true, bool on_abort_signal_parent = true); //! Change state to 'bs_run'. May only be called after creation or after returning from finish(). // Does not cause a callback. @@ -352,7 +354,7 @@ class AIStateMachine { bool waiting(void) const { return mState == bs_run && mIdle; } // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool. - typedef state_type AIStateMachine::* const bool_type; + typedef volatile state_type AIStateMachine::* const bool_type; //! Return true if state machine successfully finished. operator bool_type() const { return ((mState == bs_initialize || mState == bs_callback) && !mAborted) ? &AIStateMachine::mRunState : 0; } @@ -360,9 +362,14 @@ class AIStateMachine { char const* state_str(state_type state); private: + static void add_continued_statemachines(void); static void mainloop(void*); void multiplex(U64 current_time); + public: + //! Abort all running state machines and then run mainloop until all state machines are idle (called when application is exiting). + static void flush(void); + protected: //--------------------------------------- // Derived class implementations. From b2c71c099f4f0cc1b7b4172fea5ba8396d4bc6c0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 16:57:47 +0200 Subject: [PATCH 023/123] Fixed typo in comment --- indra/llcrashlogger/llcrashlogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index d6f8995d9..c42697ad9 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -371,7 +371,7 @@ void LLCrashLogger::updateApplication(const std::string& message) bool LLCrashLogger::init() { - // Initialized curl + // Initialize curl AICurlInterface::initCurl(); // We assume that all the logs we're looking for reside on the current drive From a6bb2604f67921fc505fee33e9e88e68a628222f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 16:58:47 +0200 Subject: [PATCH 024/123] Use our API, which makes more sense. --- indra/llmessage/llpumpio.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index 498914d33..b498e5e2b 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -1180,9 +1180,7 @@ void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout) LLMemType m1(LLMemType::MTYPE_IO_PUMP); if(timeout > 0.0f) { - mTimer.start(); - mTimer.reset(); - mTimer.setTimerExpirySec(timeout); + mTimer.start(timeout); } else { From 14e5b4668734e15ff15daeaf1cd70c73d63aa5fc Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 22:46:38 +0200 Subject: [PATCH 025/123] Fixed and adjusted remainders of isValid() code. Note that in the code, and still, has_curl_request was always false. However, instead of deleting all code paths that are only executed when has_curl_request would be true, I fixed the code to work as intended with my current implementation; which also results in LLCurlRequests to never expire. This way things won't break unexpectedly when this ever changes. Since on this branch isValid was only called still (the rest was removed already) to check if the curl download expired, I took the liberty to rename isValid to hasNotExpired. --- indra/llmessage/aicurl.h | 3 +++ indra/llmessage/aicurlprivate.h | 8 ++++++++ indra/llmessage/lliopipe.cpp | 9 ++++++++- indra/llmessage/lliopipe.h | 11 ++++++++++- indra/llmessage/llpumpio.cpp | 32 ++++++++++++++++++++------------ indra/llmessage/llpumpio.h | 5 ++--- indra/llmessage/llurlrequest.cpp | 21 ++++++++++++++++----- indra/llmessage/llurlrequest.h | 3 ++- 8 files changed, 69 insertions(+), 23 deletions(-) diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index a9a2a9d64..ac89fdac3 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -274,6 +274,9 @@ class AICurlEasyRequest { // Queue a command to remove this request from the multi session (or cancel a queued command to add it). void removeRequest(void); + // Returns true when this AICurlEasyRequest wraps a AICurlPrivate::ThreadSafeBufferedCurlEasyRequest. + bool isBuffered(void) const { return mCurlEasyRequest->isBuffered(); } + private: // The actual pointer to the ThreadSafeCurlEasyRequest instance. AICurlPrivate::CurlEasyRequestPtr mCurlEasyRequest; diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 892228bae..0bed1de7f 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -318,6 +318,10 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents { public: // Return pointer to the ThreadSafe (wrapped) version of this object. ThreadSafeBufferedCurlEasyRequest* get_lockobj(void); + + // Return true when prepRequest was already called and the object has not been + // invalidated as a result of calling timed_out(). + bool isValid(void) const { return mResponder; } }; // This class wraps CurlEasyRequest for thread-safety and adds a reference counter so we can @@ -333,6 +337,8 @@ class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple { virtual ~ThreadSafeCurlEasyRequest() { Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); } + /*virtual*/ bool isBuffered(void) const { return false; } + private: LLAtomicU32 mReferenceCount; @@ -351,6 +357,8 @@ class ThreadSafeBufferedCurlEasyRequest : public ThreadSafeCurlEasyRequest, publ public: // Throws AICurlNoEasyHandle. ThreadSafeBufferedCurlEasyRequest(void) { new (AIThreadSafeSimple::ptr()) CurlResponderBuffer; } + + /*virtual*/ bool isBuffered(void) const { return true; } }; // The curl easy request type wrapped in a reference counting pointer. diff --git a/indra/llmessage/lliopipe.cpp b/indra/llmessage/lliopipe.cpp index 8f827f7a3..c8bef05c6 100644 --- a/indra/llmessage/lliopipe.cpp +++ b/indra/llmessage/lliopipe.cpp @@ -76,7 +76,14 @@ LLIOPipe::~LLIOPipe() } //virtual -bool LLIOPipe::isValid() +bool LLIOPipe::hasExpiration(void) const +{ + // LLIOPipe::hasNotExpired always returns true. + return false; +} + +//virtual +bool LLIOPipe::hasNotExpired(void) const { return true ; } diff --git a/indra/llmessage/lliopipe.h b/indra/llmessage/lliopipe.h index 2def3229f..86ae7e425 100644 --- a/indra/llmessage/lliopipe.h +++ b/indra/llmessage/lliopipe.h @@ -231,7 +231,16 @@ public: */ virtual ~LLIOPipe(); - virtual bool isValid() ; + /** + * @brief External expiration facility. + * + * If hasExpiration() returns true, then we need to check hasNotExpired() + * to see if the LLIOPipe is still valid. In the legacy LL code the + * latter was called isValid() and was overloaded for two purposes: + * either expiration or failure to initialize. + */ + virtual bool hasExpiration(void) const; + virtual bool hasNotExpired(void) const; protected: /** diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index b498e5e2b..c249d7132 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -198,19 +198,25 @@ LLPumpIO::~LLPumpIO() } } -bool LLPumpIO::addChain(const chain_t& chain, F32 timeout, bool has_curl_request) +bool LLPumpIO::addChain(chain_t const& chain, F32 timeout) { LLMemType m1(LLMemType::MTYPE_IO_PUMP); - if(chain.empty()) return false; -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); -#endif + chain_t::const_iterator it = chain.begin(); + chain_t::const_iterator const end = chain.end(); + if (it == end) return false; + LLChainInfo info; - info.mHasCurlRequest = has_curl_request; + for(; it != end; ++it) + { + if ((*it)->hasExpiration()) + { + info.mHasExpiration = true; + break; + } + } info.setTimeoutSeconds(timeout); info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray); - info.mData->setThreaded(has_curl_request); LLLinkInfo link; #if LL_DEBUG_PIPE_TYPE_IN_PUMP lldebugs << "LLPumpIO::addChain() " << chain[0] << " '" @@ -218,14 +224,16 @@ bool LLPumpIO::addChain(const chain_t& chain, F32 timeout, bool has_curl_request #else lldebugs << "LLPumpIO::addChain() " << chain[0] <nextChannel(); info.mChainLinks.push_back(link); } +#if LL_THREADS_APR + LLScopedLock lock(mChainsMutex); +#endif mPendingChains.push_back(info); return true; } @@ -1086,14 +1094,14 @@ void LLPumpIO::processChain(LLChainInfo& chain) bool LLPumpIO::isChainExpired(LLChainInfo& chain) { - if(!chain.mHasCurlRequest) + if(!chain.mHasExpiration) { return false ; } for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter) { - if(!(*iter).mPipe->isValid()) + if(!(*iter).mPipe->hasNotExpired()) { return true ; } @@ -1168,7 +1176,7 @@ LLPumpIO::LLChainInfo::LLChainInfo() : mInit(false), mLock(0), mEOS(false), - mHasCurlRequest(false), + mHasExpiration(false), mDescriptorsPool(new LLAPRPool(LLThread::tldata().mRootPool)) { LLMemType m1(LLMemType::MTYPE_IO_PUMP); diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h index 3771be9cf..0d1387257 100644 --- a/indra/llmessage/llpumpio.h +++ b/indra/llmessage/llpumpio.h @@ -100,10 +100,9 @@ public: * @param chain The pipes for the chain * @param timeout The number of seconds in the future to * expire. Pass in 0.0f to never expire. - * @param has_curl_request The chain contains LLURLRequest if true. * @return Returns true if anything was added to the pump. */ - bool addChain(const chain_t& chain, F32 timeout, bool has_curl_request = false); + bool addChain(const chain_t& chain, F32 timeout); /** * @brief Struct to associate a pipe with it's buffer io indexes. @@ -347,7 +346,7 @@ protected: // basic member data bool mInit; bool mEOS; - bool mHasCurlRequest; + bool mHasExpiration; S32 mLock; LLFrameTimer mTimer; links_t::iterator mHead; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 97a8e48d9..9d5b18074 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -260,11 +260,22 @@ void LLURLRequest::allowCookies() } //virtual -bool LLURLRequest::isValid() +bool LLURLRequest::hasExpiration(void) const { - //FIXME - wtf is with this isValid? - //return mDetail->mCurlRequest->isValid(); - return true; + // Currently, this ALWAYS returns false -- because only AICurlEasyRequestStateMachine uses buffered + // AICurlEasyRequest objects, and LLURLRequest uses (unbuffered) AICurlEasyRequest directly, which + // have no expiration facility. + return mDetail->mCurlEasyRequest.isBuffered(); +} + +//virtual +bool LLURLRequest::hasNotExpired(void) const +{ + if (!mDetail->mCurlEasyRequest.isBuffered()) + return true; + AICurlEasyRequest_wat buffered_easy_request_w(*mDetail->mCurlEasyRequest); + AICurlResponderBuffer_wat buffer_w(*mDetail->mCurlEasyRequest); + return buffer_w->isValid(); } // virtual @@ -274,7 +285,7 @@ LLIOPipe::EStatus LLURLRequest::handleError( { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - if(!isValid()) + if(!hasNotExpired()) { return STATUS_EXPIRED ; } diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 6cfe83a5e..d265a0dae 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -190,7 +190,8 @@ public: */ void allowCookies(); - /*virtual*/ bool isValid() ; + /*virtual*/ bool hasExpiration(void) const; + /*virtual*/ bool hasNotExpired(void) const; public: /** From 7c022d6061bcf7f7419fa2c12ac4ba0fa0679117 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 22:51:14 +0200 Subject: [PATCH 026/123] Don't crash on exit. When a new state machine was just created, so run() had already been called but it never did really run yet so running() would return false; then abort() wasn't called in flush(), causing the subsequent mainloop call to actually try and startup the state machine, which then crashed because Debug Settings mechanism is already destroyed at that point (and in general, we really don't want anything to run: it does unpredictable things). With this fix, also state machines that were just created are aborted, resulting actuall in a kill without delete, and subsequently a clean delete from the mainloop. --- indra/newview/statemachine/aistatemachine.cpp | 2 +- indra/newview/statemachine/aistatemachine.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index db181ce45..94cb9da59 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -603,7 +603,7 @@ void AIStateMachine::flush(void) for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) { AIStateMachine& statemachine(iter->statemachine()); - if (statemachine.running()) + if (statemachine.abortable()) statemachine.abort(); } for (int batch = 0;; ++batch) diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 650e7f296..237c466ea 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -350,6 +350,9 @@ class AIStateMachine { //! Return true if the derived class is running (also when we are idle). bool running(void) const { return mState == bs_run; } + //! Return true if it's safe to call abort. + bool abortable(void) const { return (mState == bs_run && (!mIdle || is_main_thread())) || mState == bs_initialize; } + //! Return true if the derived class is running but idle. bool waiting(void) const { return mState == bs_run && mIdle; } From 53e96b02c0af523184f29e65dea16b8e8fececc2 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 22:59:29 +0200 Subject: [PATCH 027/123] Bug fix. Forgot to actually make it virtual. --- indra/llmessage/aicurlprivate.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 0bed1de7f..36a8b3f23 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -337,7 +337,8 @@ class ThreadSafeCurlEasyRequest : public AIThreadSafeSimple { virtual ~ThreadSafeCurlEasyRequest() { Dout(dc::curl, "Destructing ThreadSafeCurlEasyRequest with this = " << (void*)this); } - /*virtual*/ bool isBuffered(void) const { return false; } + // Returns true if this is a base class of ThreadSafeBufferedCurlEasyRequest. + virtual bool isBuffered(void) const { return false; } private: LLAtomicU32 mReferenceCount; From a34247ebf4af8aee4b9465fd715713dc23c3fb3b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 15 Jul 2012 23:08:07 +0200 Subject: [PATCH 028/123] Bug fix. Don't test on something that belongs in an assert. is_main_thread() doesn't even exist unless --type=Debug. --- indra/newview/statemachine/aistatemachine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 237c466ea..13da6fd3d 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -351,7 +351,7 @@ class AIStateMachine { bool running(void) const { return mState == bs_run; } //! Return true if it's safe to call abort. - bool abortable(void) const { return (mState == bs_run && (!mIdle || is_main_thread())) || mState == bs_initialize; } + bool abortable(void) const { return mState == bs_run || mState == bs_initialize; } //! Return true if the derived class is running but idle. bool waiting(void) const { return mState == bs_run && mIdle; } From 9deb3e433cff98e63f46ff206d17aaaf2e8e5045 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 16 Jul 2012 22:35:04 +0200 Subject: [PATCH 029/123] LLCurlRequest time out fixes. Also some more cleanup on exit improvements. --- indra/llmessage/aicurl.cpp | 4 ++-- indra/llmessage/aicurlprivate.h | 12 ++++++++++++ indra/llmessage/aicurlthread.cpp | 7 +++++++ indra/llmessage/lliopipe.h | 2 +- indra/llmessage/llpumpio.cpp | 31 ++++++++++++++++++------------- indra/llmessage/llurlrequest.cpp | 18 ++++++++++++++++-- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 4636fae7c..85e184ef5 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -554,7 +554,7 @@ void CurlEasyHandle::handle_easy_error(CURLcode code) } // Throws AICurlNoEasyHandle. -CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL) +CurlEasyHandle::CurlEasyHandle(void) : mActiveMultiHandle(NULL), mErrorBuffer(NULL), mQueuedForRemoval(false) #ifdef SHOW_ASSERT , mRemovedPerCommand(true) #endif @@ -815,7 +815,7 @@ void CurlEasyRequest::revokeCallbacks(void) mWriteCallback = &noWriteCallback; mReadCallback = &noReadCallback; mSSLCtxCallback = &noSSLCtxCallback; - if (active() && !LLApp::isExiting()) + if (active() && !no_warning()) { llwarns << "Revoking callbacks on a still active CurlEasyRequest object!" << llendl; } diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 36a8b3f23..38ce00b5c 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -98,10 +98,17 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven // Pause and unpause a connection. CURLcode pause(int bitmask); + // Called when a request is queued for removal. In that case a race between the actual removal + // and revoking of the callbacks is harmless (and happens for the raw non-statemachine version). + void remove_queued(void) { mQueuedForRemoval = true; } + // In case it's added after being removed. + void add_queued(void) { mQueuedForRemoval = false; } + private: CURL* mEasyHandle; CURLM* mActiveMultiHandle; char* mErrorBuffer; + bool mQueuedForRemoval; // Set if the easy handle is (probably) added to the multi handle, but is queued for removal. #ifdef SHOW_ASSERT public: bool mRemovedPerCommand; // Set if mActiveMultiHandle was reset as per command from the main thread. @@ -121,6 +128,11 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven // Only valid when setErrorBuffer was called and the curl_easy function returned an error. std::string getErrorString(void) const { return mErrorBuffer ? mErrorBuffer : "(null)"; } + // Returns true when it is expected that the parent will revoke callbacks before the curl + // easy handle is removed from the multi handle; that usually happens when an external + // error demands termination of the request (ie, an expiration). + bool no_warning(void) const { return mQueuedForRemoval || LLApp::isExiting(); } + // Used for debugging purposes. bool operator==(CURL* easy_handle) const { return mEasyHandle == easy_handle; } diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 3c1ce5999..039253d6e 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -617,6 +617,7 @@ void AICurlThread::cleanup_wakeup_fds(void) void AICurlThread::wakeup_thread(void) { DoutEntering(dc::curl, "AICurlThread::wakeup_thread"); + llassert(is_main_thread()); #ifdef WINDOWS #error Missing implementation @@ -1084,6 +1085,9 @@ void stopCurlThread(void) ms_sleep(10); } Dout(dc::curl, "Curl thread" << (curlThreadIsRunning() ? " not" : "") << " stopped after " << ((100 - count) * 10) << "ms."); + // Clear the command queue, for a cleaner cleanup. + command_queue_wat command_queue_w(command_queue); + command_queue_w->clear(); } } @@ -1136,6 +1140,7 @@ void AICurlEasyRequest::addRequest(void) #endif // Add a command to add the new request to the multi session to the command queue. command_queue_w->push_back(Command(*this, cmd_add)); + AICurlEasyRequest_wat(*get())->add_queued(); } // Something was added to the queue, wake up the thread to get it. wakeUpCurlThread(); @@ -1188,6 +1193,8 @@ void AICurlEasyRequest::removeRequest(void) #endif // Add a command to remove this request from the multi session to the command queue. command_queue_w->push_back(Command(*this, cmd_remove)); + // Suppress warning that would otherwise happen if the callbacks are revoked before the curl thread removed the request. + AICurlEasyRequest_wat(*get())->remove_queued(); } // Something was added to the queue, wake up the thread to get it. wakeUpCurlThread(); diff --git a/indra/llmessage/lliopipe.h b/indra/llmessage/lliopipe.h index 86ae7e425..0e71d7a00 100644 --- a/indra/llmessage/lliopipe.h +++ b/indra/llmessage/lliopipe.h @@ -149,7 +149,7 @@ public: // The connection was lost. STATUS_LOST_CONNECTION = -5, - // The totoal process time has exceeded the timeout. + // The total process time has exceeded the timeout. STATUS_EXPIRED = -6, // Keep track of the count of codes here. diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index c249d7132..e6c6a42d6 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -207,14 +207,6 @@ bool LLPumpIO::addChain(chain_t const& chain, F32 timeout) if (it == end) return false; LLChainInfo info; - for(; it != end; ++it) - { - if ((*it)->hasExpiration()) - { - info.mHasExpiration = true; - break; - } - } info.setTimeoutSeconds(timeout); info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray); LLLinkInfo link; @@ -224,13 +216,14 @@ bool LLPumpIO::addChain(chain_t const& chain, F32 timeout) #else lldebugs << "LLPumpIO::addChain() " << chain[0] <hasExpiration(); link.mPipe = (*it); link.mChannels = info.mData->nextChannel(); info.mChainLinks.push_back(link); } + #if LL_THREADS_APR LLScopedLock lock(mChainsMutex); #endif @@ -250,11 +243,10 @@ bool LLPumpIO::addChain( // description, we need to have that description matched to a // particular buffer. if(!data) return false; - if(links.empty()) return false; + links_t::const_iterator link = links.begin(); + links_t::const_iterator const end = links.end(); + if (link == end) return false; -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); -#endif #if LL_DEBUG_PIPE_TYPE_IN_PUMP lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '" << typeid(*(links[0].mPipe)).name() << "'" << llendl; @@ -266,6 +258,17 @@ bool LLPumpIO::addChain( info.mChainLinks = links; info.mData = data; info.mContext = context; + for (; link != end; ++link) + { + if (link->mPipe->hasExpiration()) + { + info.mHasExpiration = true; + break; + } + } +#if LL_THREADS_APR + LLScopedLock lock(mChainsMutex); +#endif mPendingChains.push_back(info); return true; } @@ -1114,6 +1117,8 @@ bool LLPumpIO::handleChainError( LLChainInfo& chain, LLIOPipe::EStatus error) { + DoutEntering(dc::notice, "LLPumpIO::handleChainError(" << (void*)&chain << ", " << LLIOPipe::lookupStatusString(error) << ")"); + LLMemType m1(LLMemType::MTYPE_IO_PUMP); links_t::reverse_iterator rit; if(chain.mHead == chain.mChainLinks.end()) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 9d5b18074..8bb52c55a 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -283,10 +283,24 @@ LLIOPipe::EStatus LLURLRequest::handleError( LLIOPipe::EStatus status, LLPumpIO* pump) { + DoutEntering(dc::curl, "LLURLRequest::handleError(" << LLIOPipe::lookupStatusString(status) << ", " << (void*)pump << ")"); LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - - if(!hasNotExpired()) + + if (LL_LIKELY(!mDetail->mCurlEasyRequest.isBuffered())) // Currently always true. { + // The last reference will be deleted when the pump that this chain belongs to + // is removed from the running chains vector, upon returning from this function. + // This keeps the CurlEasyRequest object alive until the curl thread cleanly removed it. + Dout(dc::curl, "Calling mDetail->mCurlEasyRequest.removeRequest()"); + mDetail->mCurlEasyRequest.removeRequest(); + } + else if (!hasNotExpired()) + { + // The buffered version has it's own time out handling, and that already expired, + // so we can ignore the expiration of this timer (currently never happens). + // I left it here because it's what LL did (in the form if (!isValid() ...), + // and it would be relevant if this characteristic of mDetail->mCurlEasyRequest + // would change. --Aleric return STATUS_EXPIRED ; } From 900e533b4b145fb7efaf5f9c36c3224839bdc5a0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 17 Jul 2012 08:01:06 +0200 Subject: [PATCH 030/123] Remove unused call to process(). --- indra/newview/llcurlrequest.cpp | 7 ------- indra/newview/llcurlrequest.h | 2 -- indra/newview/lltexturefetch.cpp | 8 -------- 3 files changed, 17 deletions(-) diff --git a/indra/newview/llcurlrequest.cpp b/indra/newview/llcurlrequest.cpp index 9192de127..75706af6d 100644 --- a/indra/newview/llcurlrequest.cpp +++ b/indra/newview/llcurlrequest.cpp @@ -135,13 +135,6 @@ bool Request::post(std::string const& url, headers_t const& headers, LLSD const& return true; // We throw in case of problems. } -S32 Request::process(void) -{ - //FIXME: needs implementation - //DoutEntering(dc::warning, "Request::process()"); - return 0; -} - } // namespace AICurlInterface //================================================================================== diff --git a/indra/newview/llcurlrequest.h b/indra/newview/llcurlrequest.h index 59f3da981..d10c20b98 100644 --- a/indra/newview/llcurlrequest.h +++ b/indra/newview/llcurlrequest.h @@ -50,8 +50,6 @@ class Request { bool getByteRange(std::string const& url, headers_t const& headers, S32 offset, S32 length, ResponderPtr responder); bool post(std::string const& url, headers_t const& headers, std::string const& data, ResponderPtr responder, S32 time_out = 0); bool post(std::string const& url, headers_t const& headers, LLSD const& data, ResponderPtr responder, S32 time_out = 0); - - S32 process(void); }; } // namespace AICurlInterface diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a638e39fd..4462fc886 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2370,14 +2370,6 @@ void LLTextureFetch::commonUpdate() // Run a cross-thread command, if any. cmdDoWork(); #endif - - // Update Curl on same thread as mCurlGetRequest was constructed - llassert_always(mCurlGetRequest); - S32 processed = mCurlGetRequest->process(); - if (processed > 0) - { - lldebugs << "processed: " << processed << " messages." << llendl; - } } From 64b968b262c517fa8c89e2a106afc3d26c0f8c18 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 17 Jul 2012 19:27:14 +0200 Subject: [PATCH 031/123] process is no longer processing --- indra/newview/llcurrencyuimanager.cpp | 2 +- indra/newview/llfloaterbuyland.cpp | 2 +- indra/newview/llmeshrepository.cpp | 4 +++- indra/newview/lluserauth.cpp | 2 +- indra/newview/llxmlrpctransaction.cpp | 2 +- indra/newview/llxmlrpctransaction.h | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 6b72cc922..e31e9d114 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -266,7 +266,7 @@ bool LLCurrencyUIManager::Impl::checkTransaction() return false; } - if (!mTransaction->process()) + if (!mTransaction->is_finished()) { return false; } diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 852b439fe..909111ee3 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -866,7 +866,7 @@ bool LLFloaterBuyLandUI::checkTransaction() return false; } - if (!mTransaction->process()) + if (!mTransaction->is_finished()) { return false; } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0b70eb02e..d5800a4eb 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1617,7 +1617,9 @@ void LLMeshUploadThread::doWholeModelUpload() new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); do { - mCurlRequest->process(); + mCurlRequest->process(); // FIXME: This function does not exist anymore. The post() gets CPU time from AICurlEasyRequestStateMachine. + // Therefore, if we do not want to continue here unless this upload is done... no wait, that would + // be blocking and we don't want blocking... //sleep for 10ms to prevent eating a whole core apr_sleep(10000); } while (mCurlRequest->getQueued() > 0); diff --git a/indra/newview/lluserauth.cpp b/indra/newview/lluserauth.cpp index f558a3170..f2f928850 100644 --- a/indra/newview/lluserauth.cpp +++ b/indra/newview/lluserauth.cpp @@ -270,7 +270,7 @@ LLUserAuth::UserAuthcode LLUserAuth::authResponse() return mAuthResponse; } - bool done = mTransaction->process(); + bool done = mTransaction->is_finished(); if (!done) { if (LLXMLRPCTransaction::StatusDownloading == mTransaction->status(0)) diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 2282249df..b160e2c33 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -518,7 +518,7 @@ LLXMLRPCTransaction::~LLXMLRPCTransaction() delete &impl; } -bool LLXMLRPCTransaction::process() +bool LLXMLRPCTransaction::is_finished(void) const { return impl.is_finished(); } diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index 42c236265..6d70f8ff1 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -110,7 +110,7 @@ public: StatusOtherError } Status; - bool process(); + bool is_finished(void) const; // Returns true when done. Status status(int* curlCode); From 87c935881300f7999946ff3d8911d03ec8e19abb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 17 Jul 2012 19:33:12 +0200 Subject: [PATCH 032/123] Add back erroneously removed comments. --- indra/newview/llmeshrepository.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index d5800a4eb..13949eee4 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1613,6 +1613,7 @@ void LLMeshUploadThread::doWholeModelUpload() LLSD body = full_model_data["asset_resources"]; dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); LLCurlRequest::headers_t headers; + // This might throw AICurlNoEasyHandle. mCurlRequest->post(mWholeModelUploadURL, headers, body, new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); do @@ -1646,6 +1647,7 @@ void LLMeshUploadThread::requestWholeModelFee() mPendingUploads++; LLCurlRequest::headers_t headers; + // This might throw AICurlNoEasyHandle. mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut); From 648ed00ce276570c53feb6797bab99a839ea69ad Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 17 Jul 2012 19:58:34 +0200 Subject: [PATCH 033/123] Don't call gSavedSettings.getU32() and calc_clock_frequency() so often anymore. --- indra/newview/statemachine/aistatemachine.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index 94cb9da59..71a8ac47f 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -80,8 +80,14 @@ AIThreadSafeSimpleDC AIStateMachine::sMaxCount; void AIStateMachine::updateSettings(void) { - Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount"); - *AIAccess(sMaxCount) = calc_clock_frequency() * gSavedSettings.getU32("StateMachineMaxTime") / 1000; + static const LLCachedControl StateMachineMaxTime("StateMachineMaxTime", 20); + static U32 last_StateMachineMaxTime = 0; + if (last_StateMachineMaxTime != StateMachineMaxTime) + { + Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount"); + *AIAccess(sMaxCount) = calc_clock_frequency() * StateMachineMaxTime / 1000; + last_StateMachineMaxTime = StateMachineMaxTime; + } } //---------------------------------------------------------------------------- From 0204d09a897bc9db8b923bc156129347fe3ca46a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 17 Jul 2012 23:39:44 +0200 Subject: [PATCH 034/123] If curl thread is already awake, then don't write something to the pipe. --- indra/llmessage/aicurlthread.cpp | 55 +++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 039253d6e..f4632b955 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -533,6 +533,8 @@ class AICurlThread : public LLThread { public: static AICurlThread* sInstance; + LLMutex mWakeUpMutex; + bool mWakeUpFlag; // Protected by mWakeUpMutex. public: // MAIN-THREAD @@ -548,6 +550,7 @@ class AICurlThread : public LLThread protected: virtual void run(void); void wakeup(AICurlMultiHandle_wat const& multi_handle_w); + void process_commands(AICurlMultiHandle_wat const& multi_handle_w); private: // MAIN-THREAD @@ -566,7 +569,9 @@ class AICurlThread : public LLThread AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD -AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), mWakeUpFd_in(CURL_SOCKET_BAD), mWakeUpFd(CURL_SOCKET_BAD), mZeroTimeOut(0), mRunning(true) +AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), + mWakeUpFd_in(CURL_SOCKET_BAD), mWakeUpFd(CURL_SOCKET_BAD), + mZeroTimeOut(0), mRunning(true), mWakeUpFlag(false) { create_wakeup_fds(); sInstance = this; @@ -619,6 +624,14 @@ void AICurlThread::wakeup_thread(void) DoutEntering(dc::curl, "AICurlThread::wakeup_thread"); llassert(is_main_thread()); + // Try if curl thread is still awake and if so, pass the new commands directly. + if (mWakeUpMutex.tryLock()) + { + mWakeUpFlag = true; + mWakeUpMutex.unlock(); + return; + } + #ifdef WINDOWS #error Missing implementation #else @@ -679,6 +692,13 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) return; } #endif + process_commands(multi_handle_w); +} + +void AICurlThread::process_commands(AICurlMultiHandle_wat const& multi_handle_w) +{ + DoutEntering(dc::curl, "AICurlThread::process_commands(void)"); + // If we get here then the main thread called wakeup_thread() recently. for(;;) { @@ -686,7 +706,12 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) { command_queue_wat command_queue_w(command_queue); if (command_queue_w->empty()) + { + mWakeUpMutex.lock(); + mWakeUpFlag = false; + mWakeUpMutex.unlock(); break; + } // Move the next command from the queue into command_being_processed. *command_being_processed_wat(command_being_processed) = command_queue_w->front(); command_queue_w->pop_front(); @@ -745,12 +770,33 @@ void AICurlThread::run(void) int nfds = 64; #endif int ready = 0; + // Process every command in command_queue before entering select(). + for(;;) + { + mWakeUpMutex.lock(); + if (mWakeUpFlag) + { + mWakeUpMutex.unlock(); + process_commands(multi_handle_w); + continue; + } + break; + } + // wakeup_thread() is also called after setting mRunning to false. + if (!mRunning) + { + mWakeUpMutex.unlock(); + break; + } + // If we get here then mWakeUpFlag has been false since we grabbed the lock. + // We're now entering select(), during which the main thread will write to the pipe/socket + // to wake us up, because it can't get the lock. struct timeval timeout; long timeout_ms = multi_handle_w->getTimeOut(); // If no timeout is set, sleep 1 second. - if (timeout_ms < 0) + if (LL_UNLIKELY(timeout_ms < 0)) timeout_ms = 1000; - if (timeout_ms == 0) + if (LL_UNLIKELY(timeout_ms == 0)) { if (mZeroTimeOut >= 10000) { @@ -764,7 +810,7 @@ void AICurlThread::run(void) } else { - if (mZeroTimeOut >= 10000) + if (LL_UNLIKELY(mZeroTimeOut >= 10000)) llinfos << "Timeout of select() call by curl thread reset (to " << timeout_ms << " ms)." << llendl; mZeroTimeOut = 0; } @@ -788,6 +834,7 @@ void AICurlThread::run(void) } #endif ready = select(nfds, read_fd_set, write_fd_set, NULL, &timeout); + mWakeUpMutex.unlock(); #ifdef CWDEBUG static int last_ready = -2; static int last_errno = 0; From 48ae0d003d6f96ab5d1fadcd289ab33096d8794d Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 17 Jul 2012 18:04:48 -0500 Subject: [PATCH 035/123] Made the Visual Studio compiler a bit more happy with aicurl. --- indra/llmessage/aicurl.cpp | 17 ++++++-- indra/llmessage/aicurl.h | 1 + indra/llmessage/aicurlthread.cpp | 42 +++++++++++-------- indra/llmessage/aicurlthread.h | 2 +- .../en-us/panel_preferences_ascent_chat.xml | 4 +- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 85e184ef5..70b2863c8 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -123,6 +123,13 @@ void ssl_locking_function(int mode, int n, char const* file, int line) } } +#if LL_WINDOWS +static unsigned long __cdecl apr_os_thread_current_wrapper() +{ + return (unsigned long)apr_os_thread_current(); +} +#endif + #if HAVE_CRYPTO_THREADID // OpenSSL uniq id function. void ssl_id_function(CRYPTO_THREADID* thread_id) @@ -216,8 +223,12 @@ void ssl_init(void) // Setting this avoids the need for a thread-local error number facility, which is hard to check. #if HAVE_CRYPTO_THREADID CRYPTO_THREADID_set_callback(&ssl_id_function); +#else +#if LL_WINDOWS + CRYPTO_set_id_callback(&apr_os_thread_current_wrapper); #else CRYPTO_set_id_callback(&apr_os_thread_current); +#endif #endif // Dynamic locks callbacks. old_ssl_dyn_create_function = CRYPTO_get_dynlock_create_callback(); @@ -286,7 +297,7 @@ void initCurl(void (*flush_hook)()) llwarns << "libcurl's age is 0; no ares support." << llendl; } llassert_always((version_info->features & CURL_VERSION_SSL)); // SSL support, added in libcurl 7.10. - if (!(version_info->features & CURL_VERSION_ASYNCHDNS)); // Asynchronous name lookups (added in libcurl 7.10.7). + if (!(version_info->features & CURL_VERSION_ASYNCHDNS)) // Asynchronous name lookups (added in libcurl 7.10.7). { llwarns << "libcurl was not compiled with support for asynchronous name lookups!" << llendl; } @@ -1313,8 +1324,8 @@ CurlMultiHandle::~CurlMultiHandle() { curl_multi_cleanup(mMultiHandle); Stats::multi_calls++; - int total = --sTotalMultiHandles; - Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining."); + --sTotalMultiHandles; + Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << sTotalMultiHandles << " remaining."); } } // namespace AICurlPrivate diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index ac89fdac3..6ca9f3100 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -233,6 +233,7 @@ typedef AIAccess AICurlEasyRequest_wat; // Events generated by AICurlPrivate::CurlEasyHandle. struct AICurlEasyHandleEvents { + virtual ~AICurlEasyHandleEvents(){} // Events. virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0; diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index f4632b955..c848d7856 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -32,9 +32,11 @@ #include "aicurlthread.h" #include "lltimer.h" // ms_sleep #include +#if !LL_WINDOWS #include #include #include +#endif #include #undef AICurlPrivate @@ -132,7 +134,7 @@ namespace curlthread { // // mMaxFdSet is the largest filedescriptor in mFdSet or -1 if it is empty. -static size_t const MAXSIZE = std::max(1024, FD_SETSIZE); +static size_t const MAXSIZE = llmax(1024, FD_SETSIZE); // Create an empty PollSet. PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), @@ -147,10 +149,10 @@ PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), // Add filedescriptor s to the PollSet. void PollSet::add(curl_socket_t s) { - llassert_always(mNrFds < MAXSIZE); + llassert_always(mNrFds < (int)MAXSIZE); mFileDescriptors[mNrFds++] = s; #if !LL_WINDOWS - mMaxFd = std::max(mMaxFd, s); + mMaxFd = llmax(mMaxFd, s); #endif } @@ -195,7 +197,7 @@ void PollSet::remove(curl_socket_t s) curl_socket_t next = mFileDescriptors[--i]; // next = 'd' mFileDescriptors[i] = cur; // Overwrite 'd' with 'e'. #if !LL_WINDOWS - max = std::max(max, cur); // max is the maximum value in 'i' or higher. + max = llmax(max, cur); // max is the maximum value in 'i' or higher. #endif cur = next; // cur = 'd' // i NrFds = 5 @@ -214,7 +216,7 @@ void PollSet::remove(curl_socket_t s) // i NrFds = 5 // v v // index: 0 1 2 3 4 - // a b c d e // max = std::max('d', 'e') + // a b c d e // max = llmax('d', 'e') // If mNext pointed to an element before s, it should be left alone. Otherwise, if mNext pointed // to s it must now point to 'd', or if it pointed beyond 's' it must be decremented by 1. @@ -228,7 +230,7 @@ void PollSet::remove(curl_socket_t s) while (i > 0) { curl_socket_t next = mFileDescriptors[--i]; - max = std::max(max, next); + max = llmax(max, next); } mMaxFd = max; llassert(mMaxFd < s); @@ -256,7 +258,7 @@ void PollSet::remove(curl_socket_t s) mFileDescriptors[i] = cur; cur = next; } - if (mIter > i) + if (mIter > (unsigned int)i) --mIter; llassert(mIter <= mFdSet.fd_count); } @@ -278,7 +280,7 @@ inline bool PollSet::is_set(curl_socket_t fd) const inline void PollSet::clr(curl_socket_t fd) { - return FD_CLR(fd, &mFdSet); + FD_CLR(fd, &mFdSet); } // This function fills mFdSet with at most FD_SETSIZE - 1 filedescriptors, @@ -312,7 +314,7 @@ refresh_t PollSet::refresh(void) // Calculate mMaxFdSet. // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. int max = -1, i = mNext, count = 0; - while (++count < FD_SETSIZE) { max = std::max(max, mFileDescriptors[i]); if (++i == mNrFds) i = 0; } + while (++count < FD_SETSIZE) { max = llmax(max, mFileDescriptors[i]); if (++i == mNrFds) i = 0; } mMaxFdSet = max; #endif } @@ -587,9 +589,9 @@ AICurlThread::~AICurlThread() // MAIN-THREAD void AICurlThread::create_wakeup_fds(void) { -#ifdef WINDOWS -// Probably need to use sockets here, cause Windows select doesn't work for a pipe. -#error Missing implementation +#if LL_WINDOWS + // Probably need to use sockets here, cause Windows select doesn't work for a pipe. + #error Missing implementation #else int pipefd[2]; if (pipe(pipefd)) @@ -612,10 +614,14 @@ void AICurlThread::create_wakeup_fds(void) // MAIN-THREAD void AICurlThread::cleanup_wakeup_fds(void) { +#if LL_WINDOWS + #error Missing implementation +#else if (mWakeUpFd_in != CURL_SOCKET_BAD) close(mWakeUpFd_in); if (mWakeUpFd != CURL_SOCKET_BAD) close(mWakeUpFd); +#endif } // MAIN-THREAD @@ -632,8 +638,8 @@ void AICurlThread::wakeup_thread(void) return; } -#ifdef WINDOWS -#error Missing implementation +#ifdef LL_WINDOWS + #error Missing implementation #else // If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. // If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. @@ -662,8 +668,8 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) { DoutEntering(dc::curl, "AICurlThread::wakeup"); -#ifdef WINDOWS -#error Missing implementation +#ifdef LL_WINDOWS + #error Missing implementation #else // If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR]. // If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read. @@ -758,9 +764,9 @@ void AICurlThread::run(void) fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); // Calculate nfds (ignored on windows). #if !LL_WINDOWS - curl_socket_t const max_rfd = std::max(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); + curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); curl_socket_t const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); - int nfds = std::max(max_rfd, max_wfd) + 1; + int nfds = llmax(max_rfd, max_wfd) + 1; llassert(0 <= nfds && nfds <= FD_SETSIZE); llassert((max_rfd == -1) == (read_fd_set == NULL) && (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index c0f9d03f7..9bbc12c4a 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -105,7 +105,7 @@ class PollSet std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. #else - int mIter; // Index into fd_set::fd_array. + unsigned int mIter; // Index into fd_set::fd_array. #endif }; diff --git a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_chat.xml b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_chat.xml index 1c6e649e4..a93176519 100644 --- a/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_chat.xml +++ b/indra/newview/skins/default/xui/en-us/panel_preferences_ascent_chat.xml @@ -16,7 +16,7 @@ --> - Chat: + Chat: - Show links on chatting object names in chat history for: + Show links on chatting object names in chat history for: No object From 706b9c55c27f5fd908c315c21c50be8b9c178db8 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 18 Jul 2012 01:41:36 +0200 Subject: [PATCH 036/123] Moving stuff around a bit... --- indra/llmessage/aicurl.cpp | 6 +++++- indra/llmessage/aicurl.h | 3 ++- indra/llmessage/aicurlthread.cpp | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 70b2863c8..3d012cba2 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1324,8 +1324,12 @@ CurlMultiHandle::~CurlMultiHandle() { curl_multi_cleanup(mMultiHandle); Stats::multi_calls++; +#ifdef CWDEBUG + int total = --sTotalMultiHandles; + Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << total << " remaining."); +#else --sTotalMultiHandles; - Dout(dc::curl, "Called CurlMultiHandle::~CurlMultiHandle() [" << (void*)this << "], " << sTotalMultiHandles << " remaining."); +#endif } } // namespace AICurlPrivate diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index 6ca9f3100..90803e050 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -233,11 +233,12 @@ typedef AIAccess AICurlEasyRequest_wat; // Events generated by AICurlPrivate::CurlEasyHandle. struct AICurlEasyHandleEvents { - virtual ~AICurlEasyHandleEvents(){} // Events. virtual void added_to_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; virtual void finished(AICurlEasyRequest_wat& curl_easy_request_w) = 0; virtual void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w) = 0; + // Avoid compiler warning. + virtual ~AICurlEasyHandleEvents() { } }; #include "aicurlprivate.h" diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index c848d7856..f1de3f71b 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -248,8 +248,8 @@ void PollSet::remove(curl_socket_t s) // mFdSet.fd_array instead of mFileDescriptors and mIter instead of mNext. if (FD_ISSET(s, &mFdSet)) { - int i = --mFdSet.fd_count; - llassert(i >= 0); + llassert(mFdSet.fd_count > 0); + unsigned int i = --mFdSet.fd_count; curl_socket_t cur = mFdSet.fd_array[i]; while (cur != s) { @@ -258,7 +258,7 @@ void PollSet::remove(curl_socket_t s) mFileDescriptors[i] = cur; cur = next; } - if (mIter > (unsigned int)i) + if (mIter > i) --mIter; llassert(mIter <= mFdSet.fd_count); } From f772cbee5177f772547c4ab05b7993ccf2600431 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 18 Jul 2012 03:13:33 +0200 Subject: [PATCH 037/123] Don't crash upon exit if we fail to cleanup. --- indra/llmessage/aicurl.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 3d012cba2..936fb0570 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1101,6 +1101,8 @@ CurlResponderBuffer::CurlResponderBuffer() curl_easy_request_w->send_events_to(this); } +#define llmaybeerrs lllog(LLApp::isExiting() ? LLError::LEVEL_WARN : LLError::LEVEL_ERROR, NULL, NULL, false) + // The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). // The AIThreadSafeSimple is destructed first (right to left), so when we get here then the // ThreadSafeCurlEasyRequest base class of ThreadSafeBufferedCurlEasyRequest is still intact and we can create @@ -1117,9 +1119,18 @@ CurlResponderBuffer::~CurlResponderBuffer() // never called, which means that the removed_from_multi_handle event never happened. // This is definitely an internal error as it can only happen when libcurl is too slow, // in which case AICurlEasyRequestStateMachine::mTimer times out, but that already - // calls CurlResponderBuffer::timed_out(). So, this really should never happen. - llerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl; - timed_out(); + // calls CurlResponderBuffer::timed_out(). + llmaybeerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl; + if (LLApp::isExiting()) + { + // It might happen if some CurlResponderBuffer escaped clean up somehow :/ + mResponder = NULL; + } + else + { + // User chose to continue. + timed_out(); + } } } From 051263117d9c43d327d1329ba47049044629a89e Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 19 Jul 2012 00:13:43 +0200 Subject: [PATCH 038/123] Don't force TLS v1 unless needed and then warn about it. --- indra/llmessage/aicurl.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 936fb0570..f58459119 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -193,14 +193,18 @@ ssl_dyn_create_function_type old_ssl_dyn_create_function; ssl_dyn_destroy_function_type old_ssl_dyn_destroy_function; ssl_dyn_lock_function_type old_ssl_dyn_lock_function; +// Set for openssl-1.0.1...1.0.1c. +static bool need_renegotiation_hack = false; + // Initialize OpenSSL library for thread-safety. void ssl_init(void) { // The version identifier format is: MMNNFFPPS: major minor fix patch status. int const compiled_openSLL_major = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; int const compiled_openSLL_minor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; - int const linked_openSLL_major = (SSLeay() >> 28) & 0xff; - int const linked_openSLL_minor = (SSLeay() >> 20) & 0xff; + unsigned long const ssleay = SSLeay(); + int const linked_openSLL_major = (ssleay >> 28) & 0xff; + int const linked_openSLL_minor = (ssleay >> 20) & 0xff; // Check if dynamically loaded version is compatible with the one we compiled against. // As off version 1.0.0 also minor versions are compatible. if (linked_openSLL_major != compiled_openSLL_major || @@ -237,6 +241,13 @@ void ssl_init(void) CRYPTO_set_dynlock_create_callback(&ssl_dyn_create_function); CRYPTO_set_dynlock_lock_callback(&ssl_dyn_lock_function); CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); + need_renegotiation_hack = (0x10001000UL <= ssleay && ssleay < 0x10001040); + if (need_renegotiation_hack) + { + llwarns << "This version of libopenssl has a bug that we work around by forcing the TLSv1 protocol. " + "That works on Second Life, but might cause you to fail to login on some OpenSim grids. " + "Upgrade to openssl 1.0.1d or higher to avoid this warning." << llendl; + } llinfos << "Successful initialization of " << SSLeay_version(SSLEAY_VERSION) << " (0x" << std::hex << SSLeay() << ")." << llendl; } @@ -996,15 +1007,19 @@ void CurlEasyRequest::applyDefaultOptions(void) { CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); - // This option forces openssl to use TLS version 1. - // The Linden Lab servers don't support later TLS versions, and libopenssl-1.0.1c has - // a bug where renegotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), - // causing the connection to fail completely without this hack. - // For a commandline test of the same, observe the difference between: - // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug - // and - // openssl s_client -tls1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug - setopt(CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1); + if (need_renegotiation_hack) + { + // This option forces openssl to use TLS version 1. + // The Linden Lab servers don't support later TLS versions, and libopenssl-1.0.1-beta1 up till and including + // libopenssl-1.0.1c have a bug where renegotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), + // causing the connection to fail completely without this hack. + // For a commandline test of the same, observe the difference between: + // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which gets no response from the server after sending the initial data, and + // openssl s_client -tls1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which finishes the negotiation and ends with 'Verify return code: 0 (ok)' + setopt(CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1); + } setopt(CURLOPT_NOSIGNAL, 1); // The old code did this for the 'buffered' version, but I think it's nonsense. //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); From fcdf5d377e6c62df7a45e5e7390635f1f801dd8c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 19 Jul 2012 17:26:02 +0200 Subject: [PATCH 039/123] Move PollSet out of the header file --- indra/llmessage/aicurlthread.cpp | 128 +++++++++++++++++++++++-------- indra/llmessage/aicurlthread.h | 71 +---------------- 2 files changed, 101 insertions(+), 98 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index f1de3f71b..c1a6c7181 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -117,6 +117,68 @@ namespace curlthread { //----------------------------------------------------------------------------- // PollSet +int const empty = 0x1; +int const complete = 0x2; + +enum refresh_t { + not_complete_not_empty = 0, + complete_not_empty = complete, + empty_and_complete = complete|empty +}; + +class PollSet +{ + public: + PollSet(void); + + // Add/remove a filedescriptor to/from mFileDescriptors. + void add(curl_socket_t s); + void remove(curl_socket_t s); + + // Copy mFileDescriptors to an internal fd_set that is returned by access(). + // Returns if all fds could be copied (complete) and/or if the resulting fd_set is empty. + refresh_t refresh(void); + + // Return a pointer to the underlaying fd_set. + fd_set* access(void) { return &mFdSet; } + +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) + // Return the largest fd set in mFdSet by refresh. + curl_socket_t get_max_fd(void) const { return mMaxFdSet; } +#endif + + // Return true if a filedescriptor is set in mFileDescriptors (used for debugging). + bool contains(curl_socket_t s) const; + + // Return true if a filedescriptor is set in mFdSet. + bool is_set(curl_socket_t s) const; + + // Clear filedescriptor in mFdSet. + void clr(curl_socket_t fd); + + // Iterate over all file descriptors that were set by refresh and are still set in mFdSet. + void reset(void); // Reset the iterator. + curl_socket_t get(void) const; // Return next filedescriptor, or CURL_SOCKET_BAD when there are no more. + // Only valid if reset() was called after the last call to refresh(). + void next(void); // Advance to next filedescriptor. + + private: + curl_socket_t* mFileDescriptors; + int mNrFds; // The number of filedescriptors in the array. + int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). + + fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). + +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) + curl_socket_t mMaxFd; // The largest filedescriptor in the array, or CURL_SOCKET_BAD when it is empty. + curl_socket_t mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or CURL_SOCKET_BAD when it was empty. + std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. + std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. +#else + unsigned int mIter; // Index into fd_set::fd_array. +#endif +}; + // A PollSet can store at least 1024 filedescriptors, or FD_SETSIZE if that is larger than 1024 [MAXSIZE]. // The number of stored filedescriptors is mNrFds [0 <= mNrFds <= MAXSIZE]. // The largest filedescriptor is stored is mMaxFd, which is -1 iff mNrFds == 0. @@ -417,28 +479,28 @@ void PollSet::next(void) class MergeIterator { public: - MergeIterator(PollSet& readPollSet, PollSet& writePollSet); + MergeIterator(PollSet* readPollSet, PollSet* writePollSet); bool next(curl_socket_t& fd_out, int& ev_bitmask_out); private: - PollSet& mReadPollSet; - PollSet& mWritePollSet; + PollSet* mReadPollSet; + PollSet* mWritePollSet; int readIndx; int writeIndx; }; -MergeIterator::MergeIterator(PollSet& readPollSet, PollSet& writePollSet) : +MergeIterator::MergeIterator(PollSet* readPollSet, PollSet* writePollSet) : mReadPollSet(readPollSet), mWritePollSet(writePollSet), readIndx(0), writeIndx(0) { - mReadPollSet.reset(); - mWritePollSet.reset(); + mReadPollSet->reset(); + mWritePollSet->reset(); } bool MergeIterator::next(curl_socket_t& fd_out, int& ev_bitmask_out) { - curl_socket_t rfd = mReadPollSet.get(); - curl_socket_t wfd = mWritePollSet.get(); + curl_socket_t rfd = mReadPollSet->get(); + curl_socket_t wfd = mWritePollSet->get(); if (rfd == CURL_SOCKET_BAD && wfd == CURL_SOCKET_BAD) return false; @@ -447,28 +509,28 @@ bool MergeIterator::next(curl_socket_t& fd_out, int& ev_bitmask_out) { fd_out = rfd; ev_bitmask_out = CURL_CSELECT_IN | CURL_CSELECT_OUT; - mReadPollSet.next(); + mReadPollSet->next(); } else if (wfd == CURL_SOCKET_BAD || (rfd != CURL_SOCKET_BAD && rfd < wfd)) // Use and increment smaller one, unless it's CURL_SOCKET_BAD. { fd_out = rfd; ev_bitmask_out = CURL_CSELECT_IN; - mReadPollSet.next(); - if (wfd != CURL_SOCKET_BAD && mWritePollSet.is_set(rfd)) + mReadPollSet->next(); + if (wfd != CURL_SOCKET_BAD && mWritePollSet->is_set(rfd)) { ev_bitmask_out |= CURL_CSELECT_OUT; - mWritePollSet.clr(rfd); + mWritePollSet->clr(rfd); } } else { fd_out = wfd; ev_bitmask_out = CURL_CSELECT_OUT; - mWritePollSet.next(); - if (rfd != CURL_SOCKET_BAD && mReadPollSet.is_set(wfd)) + mWritePollSet->next(); + if (rfd != CURL_SOCKET_BAD && mReadPollSet->is_set(wfd)) { ev_bitmask_out |= CURL_CSELECT_IN; - mReadPollSet.clr(wfd); + mReadPollSet->clr(wfd); } } @@ -498,8 +560,8 @@ CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socke mMultiHandle(multi_handle), mEasy(easy), mSocketFd(s), mAction(CURL_POLL_NONE) { mMultiHandle.assign(s, this); - llassert(!mMultiHandle.mReadPollSet.contains(s)); - llassert(!mMultiHandle.mWritePollSet.contains(s)); + llassert(!mMultiHandle.mReadPollSet->contains(s)); + llassert(!mMultiHandle.mWritePollSet->contains(s)); set_action(action); } @@ -515,16 +577,16 @@ void CurlSocketInfo::set_action(int action) if ((toggle_action & CURL_POLL_IN)) { if ((action & CURL_POLL_IN)) - mMultiHandle.mReadPollSet.add(mSocketFd); + mMultiHandle.mReadPollSet->add(mSocketFd); else - mMultiHandle.mReadPollSet.remove(mSocketFd); + mMultiHandle.mReadPollSet->remove(mSocketFd); } if ((toggle_action & CURL_POLL_OUT)) { if ((action & CURL_POLL_OUT)) - mMultiHandle.mWritePollSet.add(mSocketFd); + mMultiHandle.mWritePollSet->add(mSocketFd); else - mMultiHandle.mWritePollSet.remove(mSocketFd); + mMultiHandle.mWritePollSet->remove(mSocketFd); } } @@ -756,22 +818,22 @@ void AICurlThread::run(void) // If mRunning is true then we can only get here if mWakeUpFd != CURL_SOCKET_BAD. llassert(mWakeUpFd != CURL_SOCKET_BAD); // Copy the next batch of file descriptors from the PollSets mFiledescriptors into their mFdSet. - multi_handle_w->mReadPollSet.refresh(); - refresh_t wres = multi_handle_w->mWritePollSet.refresh(); + multi_handle_w->mReadPollSet->refresh(); + refresh_t wres = multi_handle_w->mWritePollSet->refresh(); // Add wake up fd if any, and pass NULL to select() if a set is empty. - fd_set* read_fd_set = multi_handle_w->mReadPollSet.access(); + fd_set* read_fd_set = multi_handle_w->mReadPollSet->access(); FD_SET(mWakeUpFd, read_fd_set); - fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet.access(); + fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet->access(); // Calculate nfds (ignored on windows). #if !LL_WINDOWS - curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet.get_max_fd(), mWakeUpFd); - curl_socket_t const max_wfd = multi_handle_w->mWritePollSet.get_max_fd(); + curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet->get_max_fd(), mWakeUpFd); + curl_socket_t const max_wfd = multi_handle_w->mWritePollSet->get_max_fd(); int nfds = llmax(max_rfd, max_wfd) + 1; llassert(0 <= nfds && nfds <= FD_SETSIZE); llassert((max_rfd == -1) == (read_fd_set == NULL) && (max_wfd == -1) == (write_fd_set == NULL)); // Needed on Windows. - llassert((max_rfd == -1 || multi_handle_w->mReadPollSet.is_set(max_rfd)) && - (max_wfd == -1 || multi_handle_w->mWritePollSet.is_set(max_wfd))); + llassert((max_rfd == -1 || multi_handle_w->mReadPollSet->is_set(max_rfd)) && + (max_wfd == -1 || multi_handle_w->mWritePollSet->is_set(max_wfd))); #else int nfds = 64; #endif @@ -872,7 +934,7 @@ void AICurlThread::run(void) } else { - if (multi_handle_w->mReadPollSet.is_set(mWakeUpFd)) + if (multi_handle_w->mReadPollSet->is_set(mWakeUpFd)) { // Process commands from main-thread. This can add or remove filedescriptors from the poll sets. wakeup(multi_handle_w); @@ -902,8 +964,10 @@ void AICurlThread::run(void) //----------------------------------------------------------------------------- // MultiHandle -MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandles(0), mRunningHandles(0), mTimeOut(-1) +MultiHandle::MultiHandle(void) : mHandleAddedOrRemoved(false), mPrevRunningHandles(0), mRunningHandles(0), mTimeOut(-1), mReadPollSet(NULL), mWritePollSet(NULL) { + mReadPollSet = new PollSet; + mWritePollSet = new PollSet; check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETFUNCTION, &MultiHandle::socket_callback)); check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_SOCKETDATA, this)); check_multi_code(curl_multi_setopt(mMultiHandle, CURLMOPT_TIMERFUNCTION, &MultiHandle::timer_callback)); @@ -920,6 +984,8 @@ MultiHandle::~MultiHandle() { remove_easy_request(*iter); } + delete mWritePollSet; + delete mReadPollSet; } #ifdef CWDEBUG diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index 9bbc12c4a..a2bd36352 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -39,76 +39,13 @@ namespace AICurlPrivate { namespace curlthread { +class PollSet; + // For ordering a std::set with AICurlEasyRequest objects. struct AICurlEasyRequestCompare { bool operator()(AICurlEasyRequest const& h1, AICurlEasyRequest const& h2) { return h1.get() < h2.get(); } }; -//----------------------------------------------------------------------------- -// PollSet - -int const empty = 0x1; -int const complete = 0x2; - -enum refresh_t { - not_complete_not_empty = 0, - complete_not_empty = complete, - empty_and_complete = complete|empty -}; - -class PollSet -{ - public: - PollSet(void); - - // Add/remove a filedescriptor to/from mFileDescriptors. - void add(curl_socket_t s); - void remove(curl_socket_t s); - - // Copy mFileDescriptors to an internal fd_set that is returned by access(). - // Returns if all fds could be copied (complete) and/or if the resulting fd_set is empty. - refresh_t refresh(void); - - // Return a pointer to the underlaying fd_set. - fd_set* access(void) { return &mFdSet; } - -#if !LL_WINDOWS - // Return the largest fd set in mFdSet by refresh. - curl_socket_t get_max_fd(void) const { return mMaxFdSet; } -#endif - - // Return true if a filedescriptor is set in mFileDescriptors (used for debugging). - bool contains(curl_socket_t s) const; - - // Return true if a filedescriptor is set in mFdSet. - bool is_set(curl_socket_t s) const; - - // Clear filedescriptor in mFdSet. - void clr(curl_socket_t fd); - - // Iterate over all file descriptors that were set by refresh and are still set in mFdSet. - void reset(void); // Reset the iterator. - curl_socket_t get(void) const; // Return next filedescriptor, or CURL_SOCKET_BAD when there are no more. - // Only valid if reset() was called after the last call to refresh(). - void next(void); // Advance to next filedescriptor. - - private: - curl_socket_t* mFileDescriptors; - int mNrFds; // The number of filedescriptors in the array. - int mNext; // The index of the first file descriptor to start copying, the next call to refresh(). - - fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). - -#if !LL_WINDOWS - curl_socket_t mMaxFd; // The largest filedescriptor in the array, or CURL_SOCKET_BAD when it is empty. - curl_socket_t mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or CURL_SOCKET_BAD when it was empty. - std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. - std::vector::iterator mIter; // Index into mCopiedFileDescriptors for next(); loop variable. -#else - unsigned int mIter; // Index into fd_set::fd_array. -#endif -}; - //----------------------------------------------------------------------------- // MultiHandle @@ -161,8 +98,8 @@ class MultiHandle : public CurlMultiHandle //----------------------------------------------------------------------------- // Curl socket administration: - PollSet mReadPollSet; - PollSet mWritePollSet; + PollSet* mReadPollSet; + PollSet* mWritePollSet; }; } // namespace curlthread From 2fa933409048ab43810ade0a556fddb32011b3b2 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 19 Jul 2012 17:27:37 +0200 Subject: [PATCH 040/123] Debug code to test the windows code path on linux --- indra/llmessage/aicurlthread.cpp | 136 +++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 15 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index c1a6c7181..95994a7bd 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -39,6 +39,112 @@ #endif #include +#define DEBUG_WINDOWS_CODE_ON_LINUX 1 + +#if DEBUG_WINDOWS_CODE_ON_LINUX + +struct windows_fd_set { + unsigned int fd_count; + curl_socket_t fd_array[64]; +}; + +unsigned int const not_found = (unsigned int)-1; + +static inline unsigned int find_fd(curl_socket_t s, windows_fd_set const* fsp) +{ + for (unsigned int i = 0; i < fsp->fd_count; ++i) + { + if (fsp->fd_array[i] == s) + return i; + } + return not_found; +} + +static int windows_select(int, windows_fd_set* readfds, windows_fd_set* writefds, windows_fd_set*, timeval* val) +{ + fd_set r; + fd_set w; + FD_ZERO(&r); + FD_ZERO(&w); + int mfd = -1; + if (readfds) + { + for (int i = 0; i < readfds->fd_count; ++i) + { + int fd = readfds->fd_array[i]; + FD_SET(fd, &r); + mfd = llmax(mfd, fd); + } + } + if (writefds) + { + for (int i = 0; i < writefds->fd_count; ++i) + { + int fd = writefds->fd_array[i]; + FD_SET(fd, &w); + mfd = llmax(mfd, fd); + } + } + int nfds = select(mfd + 1, readfds ? &r : NULL, writefds ? &w : NULL, NULL, val); + if (readfds) + { + unsigned int fd_count = 0; + for (int i = 0; i < readfds->fd_count; ++i) + { + if (FD_ISSET(readfds->fd_array[i], &r)) + readfds->fd_array[fd_count++] = readfds->fd_array[i]; + } + readfds->fd_count = fd_count; + } + if (writefds) + { + unsigned int fd_count = 0; + for (int i = 0; i < writefds->fd_count; ++i) + { + if (FD_ISSET(writefds->fd_array[i], &w)) + writefds->fd_array[fd_count++] = writefds->fd_array[i]; + } + writefds->fd_count = fd_count; + } + return nfds; +} + +#undef FD_SETSIZE +#undef FD_ZERO +#undef FD_ISSET +#undef FD_SET +#undef FD_CLR + +int const FD_SETSIZE = sizeof(windows_fd_set::fd_array) / sizeof(curl_socket_t); + +static void FD_ZERO(windows_fd_set* fsp) +{ + fsp->fd_count = 0; +} + +static bool FD_ISSET(curl_socket_t s, windows_fd_set const* fsp) +{ + return find_fd(s, fsp) != not_found; +} + +static void FD_SET(curl_socket_t s, windows_fd_set* fsp) +{ + llassert(!FD_ISSET(s, fsp)); + fsp->fd_array[fsp->fd_count++] = s; +} + +static void FD_CLR(curl_socket_t s, windows_fd_set* fsp) +{ + unsigned int i = find_fd(s, fsp); + llassert(i != not_found); + fsp->fd_array[i] = fsp->fd_array[--(fsp->fd_count)]; +} + +#define fd_set windows_fd_set +#define select windows_select + +#endif // DEBUG_WINDOWS_CODE_ON_LINUX + #undef AICurlPrivate namespace AICurlPrivate { @@ -201,7 +307,7 @@ static size_t const MAXSIZE = llmax(1024, FD_SETSIZE); // Create an empty PollSet. PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), mNrFds(0), mNext(0) -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) , mMaxFd(-1), mMaxFdSet(-1) #endif { @@ -213,7 +319,7 @@ void PollSet::add(curl_socket_t s) { llassert_always(mNrFds < (int)MAXSIZE); mFileDescriptors[mNrFds++] = s; -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) mMaxFd = llmax(mMaxFd, s); #endif } @@ -250,7 +356,7 @@ void PollSet::remove(curl_socket_t s) // index: 0 1 2 3 4 5 // a b c s d e curl_socket_t cur = mFileDescriptors[i]; // cur = 'e' -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) curl_socket_t max = -1; #endif while (cur != s) @@ -258,7 +364,7 @@ void PollSet::remove(curl_socket_t s) llassert(i > 0); curl_socket_t next = mFileDescriptors[--i]; // next = 'd' mFileDescriptors[i] = cur; // Overwrite 'd' with 'e'. -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) max = llmax(max, cur); // max is the maximum value in 'i' or higher. #endif cur = next; // cur = 'd' @@ -285,7 +391,7 @@ void PollSet::remove(curl_socket_t s) if (mNext > i) // i is where s was. --mNext; -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) // If s was the largest file descriptor, we have to update mMaxFd. if (s == mMaxFd) { @@ -302,7 +408,7 @@ void PollSet::remove(curl_socket_t s) // ALSO make sure that s is no longer set in mFdSet, or we might confuse libcurl by // calling curl_multi_socket_action for a socket that it told us to remove. -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) clr(s); #else // We have to use a custom implementation here, because we don't want to invalidate mIter. @@ -351,13 +457,13 @@ inline void PollSet::clr(curl_socket_t fd) refresh_t PollSet::refresh(void) { FD_ZERO(&mFdSet); -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) mCopiedFileDescriptors.clear(); #endif if (mNrFds == 0) { -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) mMaxFdSet = -1; #endif return empty_and_complete; @@ -372,7 +478,7 @@ refresh_t PollSet::refresh(void) if (mNrFds >= FD_SETSIZE) { llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) // Calculate mMaxFdSet. // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. int max = -1, i = mNext, count = 0; @@ -383,7 +489,7 @@ refresh_t PollSet::refresh(void) else { mNext = 0; // Start at the beginning if we copy everything anyway. -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) mMaxFdSet = mMaxFd; #endif } @@ -397,7 +503,7 @@ refresh_t PollSet::refresh(void) return not_complete_not_empty; } FD_SET(mFileDescriptors[i], &mFdSet); -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) mCopiedFileDescriptors.push_back(mFileDescriptors[i]); #endif if (++i == mNrFds) @@ -435,7 +541,7 @@ refresh_t PollSet::refresh(void) void PollSet::reset(void) { -#if LL_WINDOWS +#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX mIter = 0; #else if (mCopiedFileDescriptors.empty()) @@ -451,7 +557,7 @@ void PollSet::reset(void) inline curl_socket_t PollSet::get(void) const { -#if LL_WINDOWS +#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX return (mIter >= mFdSet.fd_count) ? CURL_SOCKET_BAD : mFdSet.fd_array[mIter]; #else return (mIter == mCopiedFileDescriptors.end()) ? CURL_SOCKET_BAD : *mIter; @@ -460,7 +566,7 @@ inline curl_socket_t PollSet::get(void) const void PollSet::next(void) { -#if LL_WINDOWS +#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX llassert(mIter < mFdSet.fd_count); ++mIter; #else @@ -825,7 +931,7 @@ void AICurlThread::run(void) FD_SET(mWakeUpFd, read_fd_set); fd_set* write_fd_set = ((wres & empty)) ? NULL : multi_handle_w->mWritePollSet->access(); // Calculate nfds (ignored on windows). -#if !LL_WINDOWS +#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet->get_max_fd(), mWakeUpFd); curl_socket_t const max_wfd = multi_handle_w->mWritePollSet->get_max_fd(); int nfds = llmax(max_rfd, max_wfd) + 1; From 9e5cbf330fb345ea454c22e44afbf8e347250bed Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 19 Jul 2012 17:28:45 +0200 Subject: [PATCH 041/123] Bug fix for windows code found with previous commit. --- indra/llmessage/aicurlthread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 95994a7bd..2bb33e1a5 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -39,7 +39,7 @@ #endif #include -#define DEBUG_WINDOWS_CODE_ON_LINUX 1 +#define DEBUG_WINDOWS_CODE_ON_LINUX 0 #if DEBUG_WINDOWS_CODE_ON_LINUX @@ -422,8 +422,8 @@ void PollSet::remove(curl_socket_t s) while (cur != s) { llassert(i > 0); - curl_socket_t next = mFileDescriptors[--i]; - mFileDescriptors[i] = cur; + curl_socket_t next = mFdSet.fd_array[--i]; + mFdSet.fd_array[i] = cur; cur = next; } if (mIter > i) From 3a30f1dc71a1e9047ee431ff01521fefe2d70f86 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 21 Jul 2012 03:57:58 +0200 Subject: [PATCH 042/123] This is called when LLApp::sStatus == LLApp::APP_STATUS_STOPPED too. --- indra/llmessage/aicurl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index f58459119..f11e8bdb3 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1116,7 +1116,7 @@ CurlResponderBuffer::CurlResponderBuffer() curl_easy_request_w->send_events_to(this); } -#define llmaybeerrs lllog(LLApp::isExiting() ? LLError::LEVEL_WARN : LLError::LEVEL_ERROR, NULL, NULL, false) +#define llmaybeerrs lllog(LLApp::isRunning ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false) // The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). // The AIThreadSafeSimple is destructed first (right to left), so when we get here then the From fb38f6adea57aa67c1304f0d3079f0f494745f34 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 21 Jul 2012 21:51:24 +0200 Subject: [PATCH 043/123] Always write curl I/O debug info for the login attempt. --- indra/llmessage/aicurl.cpp | 2 ++ indra/newview/llstartup.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index f11e8bdb3..8d696acba 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -887,6 +887,8 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t marker << (void*)request->get_lockobj(); libcw_do.push_marker(); libcw_do.marker().assign(marker.str().data(), marker.str().size()); + if (!debug::channels::dc::curlio.is_on()) + debug::channels::dc::curlio.on(); LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) switch (infotype) { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 15ca87ad7..10f10cc90 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -253,7 +253,9 @@ extern S32 gStartImageHeight; // local globals // - +#ifdef CWDEBUG +static bool gCurlIo; +#endif static LLHost gAgentSimHost; static BOOL gSkipOptionalUpdate = FALSE; @@ -1334,6 +1336,9 @@ bool idle_startup() llinfos << "Authenticating with " << grid_uri << llendl; + // Always write curl I/O debug info for the login attempt. + Debug(gCurlIo = dc::curl.is_on() && !dc::curlio.is_on(); if (gCurlIo) dc::curlio.on()); + // TODO if statement here to use web_login_key // OGPX : which routine would this end up in? the LLSD or XMLRPC, or ....? LLUserAuth::getInstance()->authenticate( @@ -1408,6 +1413,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "downloading..." << LL_ENDL; return FALSE; } + Debug(if (gCurlIo) dc::curlio.off()); // Login succeeded: restore dc::curlio to original state. LLStartUp::setStartupState( STATE_LOGIN_PROCESS_RESPONSE ); progress += 0.01f; set_startup_status(progress, LLTrans::getString("LoginProcessingResponse"), auth_message); From ed4c6b7c92989c355e89b8ac38c510bddd964e62 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 23 Jul 2012 18:15:11 +0200 Subject: [PATCH 044/123] Removed dead code. This code has been in the viewer source for a long time, and hasn't been used for a long time (furtherest back that I checked was Snowglobe 1.4). Most notably, this removes LLContextURLExtractor and code that used it because that required an API where AICurlEasyHandle is created before an url is known, which gets in the way of reusing connections. --- indra/llmessage/CMakeLists.txt | 4 - indra/llmessage/llsdrpcclient.cpp | 255 ----- indra/llmessage/llsdrpcclient.h | 329 ------ indra/llmessage/llsdrpcserver.cpp | 347 ------- indra/llmessage/llsdrpcserver.h | 360 ------- indra/llmessage/llurlrequest.cpp | 37 - indra/llmessage/llurlrequest.h | 44 - indra/test/CMakeLists.txt | 1 - indra/test/io.cpp | 1604 ----------------------------- 9 files changed, 2981 deletions(-) delete mode 100644 indra/llmessage/llsdrpcclient.cpp delete mode 100644 indra/llmessage/llsdrpcclient.h delete mode 100644 indra/llmessage/llsdrpcserver.cpp delete mode 100644 indra/llmessage/llsdrpcserver.h delete mode 100644 indra/test/io.cpp diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index fbf872c7f..b2b8eb4ec 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -67,8 +67,6 @@ set(llmessage_SOURCE_FILES llsdmessage.cpp llsdmessagebuilder.cpp llsdmessagereader.cpp - llsdrpcclient.cpp - llsdrpcserver.cpp llservicebuilder.cpp llservice.cpp llstoredmessage.cpp @@ -168,8 +166,6 @@ set(llmessage_HEADER_FILES llsdmessage.h llsdmessagebuilder.h llsdmessagereader.h - llsdrpcclient.h - llsdrpcserver.h llservice.h llservicebuilder.h llstoredmessage.h diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp deleted file mode 100644 index dbc511f9f..000000000 --- a/indra/llmessage/llsdrpcclient.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/** - * @file llsdrpcclient.cpp - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation of the llsd client classes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#include "linden_common.h" -#include "llsdrpcclient.h" - -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llfiltersd2xmlrpc.h" -#include "llmemtype.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llurlrequest.h" - -/** - * String constants - */ -static std::string LLSDRPC_RESPONSE_NAME("response"); -static std::string LLSDRPC_FAULT_NAME("fault"); - -/** - * LLSDRPCResponse - */ -LLSDRPCResponse::LLSDRPCResponse() : - mIsError(false), - mIsFault(false) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -// virtual -LLSDRPCResponse::~LLSDRPCResponse() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -bool LLSDRPCResponse::extractResponse(const LLSD& sd) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - bool rv = true; - if(sd.has(LLSDRPC_RESPONSE_NAME)) - { - mReturnValue = sd[LLSDRPC_RESPONSE_NAME]; - mIsFault = false; - } - else if(sd.has(LLSDRPC_FAULT_NAME)) - { - mReturnValue = sd[LLSDRPC_FAULT_NAME]; - mIsFault = true; - } - else - { - mReturnValue.clear(); - mIsError = true; - rv = false; - } - return rv; -} - -static LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response"); - -// virtual -LLIOPipe::EStatus LLSDRPCResponse::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLFastTimer t(FTM_SDRPC_RESPONSE); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - if(mIsError) - { - error(pump); - } - else if(mIsFault) - { - fault(pump); - } - else - { - response(pump); - } - PUMP_DEBUG; - return STATUS_DONE; -} - -/** - * LLSDRPCClient - */ - -LLSDRPCClient::LLSDRPCClient() : - mState(STATE_NONE), - mQueue(EPBQ_PROCESS) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -// virtual -LLSDRPCClient::~LLSDRPCClient() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - //llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")" - // << llendl; - if(method.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2; - LLSDSerialize::toNotation(parameter, req); - req << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -bool LLSDRPCClient::call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - //llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")" - // << llendl; - if(method.empty() || parameter.empty() || !response) - { - return false; - } - mState = STATE_READY; - mURI.assign(uri); - std::stringstream req; - req << LLSDRPC_REQUEST_HEADER_1 << method - << LLSDRPC_REQUEST_HEADER_2 << parameter - << LLSDRPC_REQUEST_FOOTER; - mRequest = req.str(); - mQueue = queue; - mResponse = response; - return true; -} - -static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_CLIENT("SDRPC Client"); - -// virtual -LLIOPipe::EStatus LLSDRPCClient::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); - if((STATE_NONE == mState) || (!pump)) - { - // You should have called the call() method already. - return STATUS_PRECONDITION_NOT_MET; - } - EStatus rv = STATUS_DONE; - switch(mState) - { - case STATE_READY: - { - PUMP_DEBUG; -// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl; - buffer->append( - channels.out(), - (U8*)mRequest.c_str(), - mRequest.length()); - context[CONTEXT_DEST_URI_SD_LABEL] = mURI; - mState = STATE_WAITING_FOR_RESPONSE; - break; - } - case STATE_WAITING_FOR_RESPONSE: - { - PUMP_DEBUG; - // The input channel has the sd response in it. - //lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" - // << llendl; - LLBufferStream resp(channels, buffer.get()); - LLSD sd; - LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); - LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); - if (!response) - { - mState = STATE_DONE; - break; - } - response->extractResponse(sd); - if(EPBQ_PROCESS == mQueue) - { - LLPumpIO::chain_t chain; - chain.push_back(mResponse); - pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS); - } - else - { - pump->respond(mResponse.get()); - } - mState = STATE_DONE; - break; - } - case STATE_DONE: - default: - PUMP_DEBUG; - llinfos << "invalid state to process" << llendl; - rv = STATUS_ERROR; - break; - } - return rv; -} diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h deleted file mode 100644 index 80fa82dcc..000000000 --- a/indra/llmessage/llsdrpcclient.h +++ /dev/null @@ -1,329 +0,0 @@ -/** - * @file llsdrpcclient.h - * @author Phoenix - * @date 2005-11-05 - * @brief Implementation and helpers for structure data RPC clients. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDRPCCLIENT_H -#define LL_LLSDRPCCLIENT_H - -/** - * This file declares classes to encapsulate a basic structured data - * remote procedure client. - */ - -#include "llchainio.h" -#include "llfiltersd2xmlrpc.h" -#include "lliopipe.h" -#include "llurlrequest.h" - -/** - * @class LLSDRPCClientResponse - * @brief Abstract base class to represent a response from an SD server. - * - * This is used as a base class for callbacks generated from an - * structured data remote procedure call. The - * extractResponse method will deal with the llsdrpc method - * call overhead, and keep track of what to call during the next call - * into process. If you use this as a base class, you - * need to implement response, fault, and - * error to do something useful. When in those methods, - * you can parse and utilize the mReturnValue member data. - */ -class LLSDRPCResponse : public LLIOPipe -{ -public: - LLSDRPCResponse(); - virtual ~LLSDRPCResponse(); - - /** - * @brief This method extracts the response out of the sd passed in - * - * Any appropriate data found in the sd passed in will be - * extracted and managed by this object - not copied or cloned. It - * will still be up to the caller to delete the pointer passed in. - * @param sd The raw structured data response from the remote server. - * @return Returns true if this was able to parse the structured data. - */ - bool extractResponse(const LLSD& sd); - -protected: - /** - * @brief Method called when the response is ready. - */ - virtual bool response(LLPumpIO* pump) = 0; - - /** - * @brief Method called when a fault is generated by the remote server. - */ - virtual bool fault(LLPumpIO* pump) = 0; - - /** - * @brief Method called when there was an error - */ - virtual bool error(LLPumpIO* pump) = 0; - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLSD mReturnValue; - bool mIsError; - bool mIsFault; -}; - -/** - * @class LLSDRPCClient - * @brief Client class for a structured data remote procedure call. - * - * This class helps deal with making structured data calls to a remote - * server. You can visualize the calls as: - * - * response = uri.method(parameter) - * - * where you pass in everything to call and this class - * takes care of the rest of the details. - * In typical usage, you will derive a class from this class and - * provide an API more useful for the specific application at - * hand. For example, if you were writing a service to send an instant - * message, you could create an API for it to send the messsage, and - * that class would do the work of translating it into the method and - * parameter, find the destination, and invoke call with - * a useful implementation of LLSDRPCResponse passed in to handle the - * response from the network. - */ -class LLSDRPCClient : public LLIOPipe -{ -public: - LLSDRPCClient(); - virtual ~LLSDRPCClient(); - - /** - * @brief Enumeration for tracking which queue to process the - * response. - */ - enum EPassBackQueue - { - EPBQ_PROCESS, - EPBQ_CALLBACK, - }; - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The parameter to pass into the remote - * object. It is up to the caller to delete the value passed in. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const LLSD& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - - /** - * @brief Call a method on a remote LLSDRPCServer - * - * @param uri The remote object to call, eg, - * http://localhost/usher. If you are using a factory with a fixed - * url, the uri passed in will probably be ignored. - * @param method The method to call on the remote object - * @param parameter The seriailized parameter to pass into the - * remote object. - * @param response The object which gets the response. - * @param queue Specifies to call the response on the process or - * callback queue. - * @return Returns true if this object will be able to make the RPC call. - */ - bool call( - const std::string& uri, - const std::string& method, - const std::string& parameter, - LLSDRPCResponse* response, - EPassBackQueue queue); - -protected: - /** - * @brief Enumeration for tracking client state. - */ - enum EState - { - STATE_NONE, - STATE_READY, - STATE_WAITING_FOR_RESPONSE, - STATE_DONE - }; - - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - EState mState; - std::string mURI; - std::string mRequest; - EPassBackQueue mQueue; - LLIOPipe::ptr_t mResponse; -}; - -/** - * @class LLSDRPCClientFactory - * @brief Basic implementation for making an SD RPC client factory - * - * This class eases construction of a basic sd rpc client. Here is an - * example of it's use: - * - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLSDRPCClientFactory)) - * - */ -template -class LLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLSDRPCClientFactory() {} - LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLSDRPCClientFactory::build" << llendl; - LLURLRequest* http; - try - { - http = new LLURLRequest(LLURLRequest::HTTP_POST); - } - catch(AICurlNoEasyHandle const& error) - { - llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ; - return false; - } - - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader("Content-Type: text/llsd"); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(http_pipe); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -/** - * @class LLXMLSDRPCClientFactory - * @brief Basic implementation for making an XMLRPC to SD RPC client factory - * - * This class eases construction of a basic sd rpc client which uses - * xmlrpc as a serialization grammar. Here is an example of it's use: - * - * class LLUsefulService : public LLService { ... } - * LLService::registerCreator( - * "useful", - * LLService::creator_t(new LLXMLSDRPCClientFactory)) - * - */ -template -class LLXMLSDRPCClientFactory : public LLChainIOFactory -{ -public: - LLXMLSDRPCClientFactory() {} - LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {} - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLXMLSDRPCClientFactory::build" << llendl; - - LLURLRequest* http; - try - { - http = new LLURLRequest(LLURLRequest::HTTP_POST); - } - catch(AICurlNoEasyHandle const& error) - { - llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ; - return false ; - } - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLIOPipe::ptr_t http_pipe(http); - http->addHeader("Content-Type: text/xml"); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL))); - chain.push_back(http_pipe); - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - chain.push_back(service); - return true; - } -protected: - std::string mURL; -}; - -#endif // LL_LLSDRPCCLIENT_H diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp deleted file mode 100644 index 6ee5bb508..000000000 --- a/indra/llmessage/llsdrpcserver.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/** - * @file llsdrpcserver.cpp - * @author Phoenix - * @date 2005-10-11 - * @brief Implementation of the LLSDRPCServer and related classes. - * - * $LicenseInfo:firstyear=2005&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$ - */ - -#include "linden_common.h" -#include "llsdrpcserver.h" - -#include "llbuffer.h" -#include "llbufferstream.h" -#include "llfasttimer.h" -#include "llmemtype.h" -#include "llpumpio.h" -#include "llsdserialize.h" -#include "llstl.h" - -static const char FAULT_PART_1[] = "{'fault':{'code':i"; -static const char FAULT_PART_2[] = ", 'description':'"; -static const char FAULT_PART_3[] = "'}}"; - -static const char RESPONSE_PART_1[] = "{'response':"; -static const char RESPONSE_PART_2[] = "}"; - -static const S32 FAULT_GENERIC = 1000; -static const S32 FAULT_METHOD_NOT_FOUND = 1001; - -static const std::string LLSDRPC_METHOD_SD_NAME("method"); -static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter"); - - -/** - * LLSDRPCServer - */ -LLSDRPCServer::LLSDRPCServer() : - mState(LLSDRPCServer::STATE_NONE), - mPump(NULL), - mLock(0) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); -} - -LLSDRPCServer::~LLSDRPCServer() -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - std::for_each( - mMethods.begin(), - mMethods.end(), - llcompose1( - DeletePointerFunctor(), - llselect2nd())); - std::for_each( - mCallbackMethods.begin(), - mCallbackMethods.end(), - llcompose1( - DeletePointerFunctor(), - llselect2nd())); -} - - -// virtual -ESDRPCSStatus LLSDRPCServer::deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data) { - // subclass should provide a sane implementation - return ESDRPCS_DONE; -} - -void LLSDRPCServer::clearLock() -{ - if(mLock && mPump) - { - mPump->clearLock(mLock); - mPump = NULL; - mLock = 0; - } -} - -static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_SERVER("SDRPC Server"); - -// virtual -LLIOPipe::EStatus LLSDRPCServer::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLFastTimer t(FTM_PROCESS_SDRPC_SERVER); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); -// lldebugs << "LLSDRPCServer::process_impl" << llendl; - // Once we have all the data, We need to read the sd on - // the the in channel, and respond on the out channel - if(!eos) return STATUS_BREAK; - if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; - - std::string method_name; - LLIOPipe::EStatus status = STATUS_DONE; - - switch(mState) - { - case STATE_DEFERRED: - PUMP_DEBUG; - if(ESDRPCS_DONE != deferredResponse(channels, buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "deferred response failed."); - } - mState = STATE_DONE; - return STATUS_DONE; - - case STATE_DONE: -// lldebugs << "STATE_DONE" << llendl; - break; - case STATE_CALLBACK: -// lldebugs << "STATE_CALLBACK" << llendl; - PUMP_DEBUG; - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - if(ESDRPCS_DONE != callbackMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get())) - { - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Callback method call failed."); - } - } - else - { - // this should never happen, since we should not be in - // this state unless we originally found a method and - // params during the first call to process. - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Invalid LLSDRPC sever state - callback without method."); - } - pump->clearLock(mLock); - mLock = 0; - mState = STATE_DONE; - break; - case STATE_NONE: -// lldebugs << "STATE_NONE" << llendl; - default: - { - // First time we got here - process the SD request, and call - // the method. - PUMP_DEBUG; - LLBufferStream istr(channels, buffer.get()); - mRequest.clear(); - LLSDSerialize::fromNotation( - mRequest, - istr, - buffer->count(channels.in())); - - // { 'method':'...', 'parameter': ... } - method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); - if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) - { - ESDRPCSStatus rv = callMethod( - method_name, - mRequest[LLSDRPC_PARAMETER_SD_NAME], - channels, - buffer.get()); - switch(rv) - { - case ESDRPCS_DEFERRED: - mPump = pump; - mLock = pump->setLock(); - mState = STATE_DEFERRED; - status = STATUS_BREAK; - break; - - case ESDRPCS_CALLBACK: - { - mState = STATE_CALLBACK; - LLPumpIO::LLLinkInfo link; - link.mPipe = LLIOPipe::ptr_t(this); - link.mChannels = channels; - LLPumpIO::links_t links; - links.push_back(link); - pump->respond(links, buffer, context); - mLock = pump->setLock(); - status = STATUS_BREAK; - break; - } - case ESDRPCS_DONE: - mState = STATE_DONE; - break; - case ESDRPCS_ERROR: - default: - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Method call failed."); - break; - } - } - else - { - // send a fault - buildFault( - channels, - buffer.get(), - FAULT_GENERIC, - "Unable to find method and parameter in request."); - } - break; - } - } - - PUMP_DEBUG; - return status; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - // Try to find the method in the method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mMethods.find(method); - if(it != mMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - it = mCallbackMethods.find(method); - if(it == mCallbackMethods.end()) - { - // method not found. - std::ostringstream message; - message << "rpc server unable to find method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - else - { - // we found it in the callback methods - tell the process - // to coordinate calling on the pump callback. - return ESDRPCS_CALLBACK; - } - } - return rv; -} - -// virtual -ESDRPCSStatus LLSDRPCServer::callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - // Try to find the method in the callback method table. - ESDRPCSStatus rv = ESDRPCS_DONE; - method_map_t::iterator it = mCallbackMethods.find(method); - if(it != mCallbackMethods.end()) - { - rv = (*it).second->call(params, channels, response); - } - else - { - std::ostringstream message; - message << "pcserver unable to find callback method: " << method; - buildFault( - channels, - response, - FAULT_METHOD_NOT_FOUND, - message.str()); - } - return rv; -} - -// static -void LLSDRPCServer::buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - LLBufferStream ostr(channels, data); - ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3; - llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl; -} - -// static -void LLSDRPCServer::buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response) -{ - LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); - LLBufferStream ostr(channels, data); - ostr << RESPONSE_PART_1; - LLSDSerialize::toNotation(response, ostr); - ostr << RESPONSE_PART_2; -#if LL_DEBUG - std::ostringstream debug_ostr; - debug_ostr << "LLSDRPCServer::buildResponse: "; - LLSDSerialize::toNotation(response, debug_ostr); - llinfos << debug_ostr.str() << llendl; -#endif -} diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h deleted file mode 100644 index 9e56e4ea4..000000000 --- a/indra/llmessage/llsdrpcserver.h +++ /dev/null @@ -1,360 +0,0 @@ -/** - * @file llsdrpcserver.h - * @author Phoenix - * @date 2005-10-11 - * @brief Declaration of the structured data remote procedure call server. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDRPCSERVER_H -#define LL_LLSDRPCSERVER_H - -/** - * I've set this up to be pretty easy to use when you want to make a - * structured data rpc server which responds to methods by - * name. Derive a class from the LLSDRPCServer, and during - * construction (or initialization if you have the luxury) map method - * names to pointers to member functions. This will look a lot like: - * - * - * class LLMessageAgents : public LLSDRPCServer {
- * public:
- * typedef LLSDRPCServer mem_fn_t;
- * LLMessageAgents() {
- * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);
- * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);
- * }
- * protected:
- * rpc_IM(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}
- * rpc_Alert(const LLSD& params, - * const LLChannelDescriptors& channels, - * LLBufferArray* data) - * {...}
- * };
- *
- * - * The params are an array where each element in the array is a single - * parameter in the call. - * - * It is up to you to pack a valid serialized llsd response into the - * data object passed into the method, but you can use the helper - * methods below to help. - */ - -#include -#include "lliopipe.h" -#include "lliohttpserver.h" -#include "llfiltersd2xmlrpc.h" - -class LLSD; - -/** - * @brief Enumeration for specifying server method call status. This - * enumeration controls how the server class will manage the pump - * process/callback mechanism. - */ -enum ESDRPCSStatus -{ - // The call went ok, but the response is not yet ready. The - // method will arrange for the clearLock() call to be made at - // a later date, after which, once the chain is being pumped - // again, deferredResponse() will be called to gather the result - ESDRPCS_DEFERRED, - - // The LLSDRPCServer would like to handle the method on the - // callback queue of the pump. - ESDRPCS_CALLBACK, - - // The method call finished and generated output. - ESDRPCS_DONE, - - // Method failed for some unspecified reason - you should avoid - // this. A generic fault will be sent to the output. - ESDRPCS_ERROR, - - ESDRPCS_COUNT, -}; - -/** - * @class LLSDRPCMethodCallBase - * @brief Base class for calling a member function in an sd rpcserver - * implementation. - */ -class LLSDRPCMethodCallBase -{ -public: - LLSDRPCMethodCallBase() {} - virtual ~LLSDRPCMethodCallBase() {} - - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* response) = 0; -protected: -}; - -/** - * @class LLSDRPCMethodCall - * @brief Class which implements member function calls. - */ -template -class LLSDRPCMethodCall : public LLSDRPCMethodCallBase -{ -public: - typedef ESDRPCSStatus (Server::*mem_fn)( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - LLSDRPCMethodCall(Server* s, mem_fn fn) : - mServer(s), - mMemFn(fn) - { - } - virtual ~LLSDRPCMethodCall() {} - virtual ESDRPCSStatus call( - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - return (*mServer.*mMemFn)(params, channels, data); - } - -protected: - Server* mServer; - mem_fn mMemFn; - //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data); -}; - - -/** - * @class LLSDRPCServer - * @brief Basic implementation of a structure data rpc server - * - * The rpc server is also designed to appropriately straddle the pump - * process() and callback() to specify which - * thread you want to work on when handling a method call. The - * mMethods methods are called from - * process(), while the mCallbackMethods are - * called when a pump is in a callback() cycle. - */ -class LLSDRPCServer : public LLIOPipe -{ -public: - LLSDRPCServer(); - virtual ~LLSDRPCServer(); - - /** - * enumeration for generic fault codes - */ - enum - { - FAULT_BAD_REQUEST = 2000, - FAULT_NO_RESPONSE = 2001, - }; - - /** - * @brief Call this method to return an rpc fault. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param code The fault code - * @param msg The fault message - */ - static void buildFault( - const LLChannelDescriptors& channels, - LLBufferArray* data, - S32 code, - const std::string& msg); - - /** - * @brief Call this method to build an rpc response. - * - * @param channel The channel for output on the data buffer - * @param data buffer which will recieve the final output - * @param response The return value from the method call - */ - static void buildResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data, - const LLSD& response); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - - /** - * @brief Enumeration to track the state of the rpc server instance - */ - enum EState - { - STATE_NONE, - STATE_CALLBACK, - STATE_DEFERRED, - STATE_DONE - }; - - /** - * @brief This method is called when an http post comes in. - * - * The default behavior is to look at the method name, look up the - * method in the method table, and call it. If the method is not - * found, this function will build a fault response. You can - * implement your own version of this function if you want to hard - * wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief This method is called when a pump callback is processed. - * - * The default behavior is to look at the method name, look up the - * method in the callback method table, and call it. If the method - * is not found, this function will build a fault response. You - * can implement your own version of this function if you want to - * hard wire some behavior or optimize things a bit. - * @param method The method name being called - * @param params The parameters - * @param channel The channel for output on the data buffer - * @param data The http data - * @return Returns the status of the method call, done/deferred/etc - */ - virtual ESDRPCSStatus callbackMethod( - const std::string& method, - const LLSD& params, - const LLChannelDescriptors& channels, - LLBufferArray* data); - - /** - * @brief Called after a deferred service is unlocked - * - * If a method returns ESDRPCS_DEFERRED, then the service chain - * will be locked and not processed until some other system calls - * clearLock() on the service instance again. At that point, - * once the pump starts processing the chain again, this method - * will be called so the service can output the final result - * into the buffers. - */ - virtual ESDRPCSStatus deferredResponse( - const LLChannelDescriptors& channels, - LLBufferArray* data); - - // donovan put this public here 7/27/06 -public: - /** - * @brief unlock a service that as ESDRPCS_DEFERRED - */ - void clearLock(); - -protected: - EState mState; - LLSD mRequest; - LLPumpIO* mPump; - S32 mLock; - typedef std::map method_map_t; - method_map_t mMethods; - method_map_t mCallbackMethods; -}; - -/** - * @name Helper Templates for making LLHTTPNodes - * - * These templates help in creating nodes for handing a service from - * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer. - * - * To use it: - * \code - * class LLUsefulServer : public LLSDRPCServer { ... } - * - * LLHTTPNode& root = LLCreateHTTPWireServer(...); - * root.addNode("llsdrpc/useful", new LLSDRPCNode); - * root.addNode("xmlrpc/useful", new LLXMLRPCNode); - * \endcode - */ -//@{ - -template -class LLSDRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLXMLSDRPCServerFactory::build" << llendl; - chain.push_back(LLIOPipe::ptr_t(new Server)); - return true; - } -}; - -template -class LLSDRPCNode : public LLHTTPNodeForFactory< - LLSDRPCServerFactory > -{ -}; - -template -class LLXMLRPCServerFactory : public LLChainIOFactory -{ -public: - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLXMLSDRPCServerFactory::build" << llendl; - chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - chain.push_back(LLIOPipe::ptr_t(new Server)); - chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - return true; - } -}; - -template -class LLXMLRPCNode : public LLHTTPNodeForFactory< - LLXMLRPCServerFactory > -{ -}; - -//@} - -#endif // LL_LLSDRPCSERVER_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 8bb52c55a..91021186f 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -52,7 +52,6 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499; /** * String constants */ -const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri"); const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); @@ -715,42 +714,6 @@ static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void* return header_len; } -static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor"); -/** - * LLContextURLExtractor - */ -// virtual -LLIOPipe::EStatus LLContextURLExtractor::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR); - PUMP_DEBUG; - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - // The destination host is in the context. - if(context.isUndefined() || !mRequest) - { - return STATUS_PRECONDITION_NOT_MET; - } - - // copy in to out, since this just extract the URL and does not - // actually change the data. - LLChangeChannel change(channels.in(), channels.out()); - std::for_each(buffer->beginSegment(), buffer->endSegment(), change); - - // find the context url - if(context.has(CONTEXT_DEST_URI_SD_LABEL)) - { - mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString()); - return STATUS_DONE; - } - return STATUS_ERROR; -} - - /** * LLURLRequestComplete */ diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index d265a0dae..aee8a1a84 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -42,7 +42,6 @@ extern const std::string CONTEXT_REQUEST; -extern const std::string CONTEXT_DEST_URI_SD_LABEL; extern const std::string CONTEXT_RESPONSE; extern const std::string CONTEXT_TRANSFERED_BYTES; @@ -276,42 +275,6 @@ private: LLURLRequest(const LLURLRequest&); }; - -/** - * @class LLContextURLExtractor - * @brief This class unpacks the url out of a agent usher service so - * it can be packed into a LLURLRequest object. - * @see LLIOPipe - * - * This class assumes that the context is a map that contains an entry - * named CONTEXT_DEST_URI_SD_LABEL. - */ -class LLContextURLExtractor : public LLIOPipe -{ -public: - LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {} - ~LLContextURLExtractor() {} - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - -protected: - LLURLRequest* mRequest; -}; - - /** * @class LLURLRequestComplete * @brief Class which can optionally be used with an LLURLRequest to @@ -384,11 +347,4 @@ protected: EStatus mRequestStatus; }; - - -/** - * External constants - */ -extern const std::string CONTEXT_DEST_URI_SD_LABEL; - #endif // LL_LLURLREQUEST_H diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 2033d6762..de440d2df 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -28,7 +28,6 @@ include_directories( set(test_SOURCE_FILES common.cpp inventory.cpp - io.cpp # llapp_tut.cpp # Temporarily removed until thread issues can be solved llbase64_tut.cpp llblowfish_tut.cpp diff --git a/indra/test/io.cpp b/indra/test/io.cpp deleted file mode 100644 index 9f3adb28b..000000000 --- a/indra/test/io.cpp +++ /dev/null @@ -1,1604 +0,0 @@ -/** - * @file io.cpp - * @author Phoenix - * @date 2005-10-02 - * @brief Tests for io classes and helpers - * - * $LicenseInfo:firstyear=2005&license=viewergpl$ - * - * Copyright (c) 2005-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "lltut.h" - -#include - -#include "apr_pools.h" - -#include "llbuffer.h" -#include "llbufferstream.h" -#include "lliosocket.h" -#include "llioutil.h" -#include "llmemorystream.h" -#include "llpipeutil.h" -#include "llpumpio.h" -#include "llsd.h" -#include "llsdrpcclient.h" -#include "llsdrpcserver.h" -#include "llsdserialize.h" -#include "lluuid.h" -#include "llinstantmessage.h" - -namespace tut -{ - struct heap_buffer_data - { - heap_buffer_data() : mBuffer(NULL) {} - ~heap_buffer_data() { if(mBuffer) delete mBuffer; } - LLHeapBuffer* mBuffer; - }; - typedef test_group heap_buffer_test; - typedef heap_buffer_test::object heap_buffer_object; - tut::heap_buffer_test thb("heap_buffer"); - - template<> template<> - void heap_buffer_object::test<1>() - { - const S32 BUF_SIZE = 100; - mBuffer = new LLHeapBuffer(BUF_SIZE); - ensure_equals("empty buffer capacity", mBuffer->capacity(), BUF_SIZE); - const S32 SEGMENT_SIZE = 50; - LLSegment segment; - mBuffer->createSegment(0, SEGMENT_SIZE, segment); - ensure_equals("used buffer capacity", mBuffer->capacity(), BUF_SIZE); - } - - template<> template<> - void heap_buffer_object::test<2>() - { - const S32 BUF_SIZE = 10; - mBuffer = new LLHeapBuffer(BUF_SIZE); - LLSegment segment; - mBuffer->createSegment(0, BUF_SIZE, segment); - ensure("segment is in buffer", mBuffer->containsSegment(segment)); - ensure_equals("buffer consumed", mBuffer->bytesLeft(), 0); - bool created; - created = mBuffer->createSegment(0, 0, segment); - ensure("Create zero size segment fails", !created); - created = mBuffer->createSegment(0, BUF_SIZE, segment); - ensure("Create segment fails", !created); - } - - template<> template<> - void heap_buffer_object::test<3>() - { - const S32 BUF_SIZE = 10; - mBuffer = new LLHeapBuffer(BUF_SIZE); - LLSegment segment; - mBuffer->createSegment(0, BUF_SIZE, segment); - ensure("segment is in buffer", mBuffer->containsSegment(segment)); - ensure_equals("buffer consumed", mBuffer->bytesLeft(), 0); - bool reclaimed = mBuffer->reclaimSegment(segment); - ensure("buffer reclaimed.", reclaimed); - ensure_equals("buffer available", mBuffer->bytesLeft(), BUF_SIZE); - bool created; - created = mBuffer->createSegment(0, 0, segment); - ensure("Create zero size segment fails", !created); - created = mBuffer->createSegment(0, BUF_SIZE, segment); - ensure("Create another segment succeeds", created); - } - - template<> template<> - void heap_buffer_object::test<4>() - { - const S32 BUF_SIZE = 10; - const S32 SEGMENT_SIZE = 4; - mBuffer = new LLHeapBuffer(BUF_SIZE); - LLSegment seg1; - mBuffer->createSegment(0, SEGMENT_SIZE, seg1); - ensure("segment is in buffer", mBuffer->containsSegment(seg1)); - LLSegment seg2; - mBuffer->createSegment(0, SEGMENT_SIZE, seg2); - ensure("segment is in buffer", mBuffer->containsSegment(seg2)); - LLSegment seg3; - mBuffer->createSegment(0, SEGMENT_SIZE, seg3); - ensure("segment is in buffer", mBuffer->containsSegment(seg3)); - ensure_equals("segment is truncated", seg3.size(), 2); - LLSegment seg4; - bool created; - created = mBuffer->createSegment(0, SEGMENT_SIZE, seg4); - ensure("Create segment fails", !created); - bool reclaimed; - reclaimed = mBuffer->reclaimSegment(seg1); - ensure("buffer reclaim succeed.", reclaimed); - ensure_equals("no buffer available", mBuffer->bytesLeft(), 0); - reclaimed = mBuffer->reclaimSegment(seg2); - ensure("buffer reclaim succeed.", reclaimed); - ensure_equals("buffer reclaimed", mBuffer->bytesLeft(), 0); - reclaimed = mBuffer->reclaimSegment(seg3); - ensure("buffer reclaim succeed.", reclaimed); - ensure_equals("buffer reclaimed", mBuffer->bytesLeft(), BUF_SIZE); - created = mBuffer->createSegment(0, SEGMENT_SIZE, seg1); - ensure("segment is in buffer", mBuffer->containsSegment(seg1)); - ensure("Create segment succeds", created); - } -} - -namespace tut -{ - struct buffer_data - { - LLBufferArray mBuffer; - }; - typedef test_group buffer_test; - typedef buffer_test::object buffer_object; - tut::buffer_test tba("buffer_array"); - - template<> template<> - void buffer_object::test<1>() - { - const char HELLO_WORLD[] = "hello world"; - const S32 str_len = strlen(HELLO_WORLD); - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len); - S32 count = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("total append size", count, str_len); - LLBufferArray::segment_iterator_t it = mBuffer.beginSegment(); - U8* first = (*it).data(); - count = mBuffer.countAfter(ch.in(), first); - ensure_equals("offset append size", count, str_len - 1); - } - - template<> template<> - void buffer_object::test<2>() - { - const char HELLO_WORLD[] = "hello world"; - const S32 str_len = strlen(HELLO_WORLD); /* Flawfinder: ignore */ - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len); - mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len); - S32 count = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("total append size", count, 2 * str_len); - LLBufferArray::segment_iterator_t it = mBuffer.beginSegment(); - U8* first = (*it).data(); - count = mBuffer.countAfter(ch.in(), first); - ensure_equals("offset append size", count, (2 * str_len) - 1); - } - - template<> template<> - void buffer_object::test<3>() - { - const char ONE[] = "one"; - const char TWO[] = "two"; - std::string expected(ONE); - expected.append(TWO); - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)ONE, 3); - mBuffer.append(ch.in(), (U8*)TWO, 3); - char buffer[255]; /* Flawfinder: ignore */ - S32 len = 6; - mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len); - ensure_equals(len, 6); - buffer[len] = '\0'; - std::string actual(buffer); - ensure_equals("read", actual, expected); - } - - template<> template<> - void buffer_object::test<4>() - { - const char ONE[] = "one"; - const char TWO[] = "two"; - std::string expected(ONE); - expected.append(TWO); - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)TWO, 3); - mBuffer.prepend(ch.in(), (U8*)ONE, 3); - char buffer[255]; /* Flawfinder: ignore */ - S32 len = 6; - mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len); - ensure_equals(len, 6); - buffer[len] = '\0'; - std::string actual(buffer); - ensure_equals("read", actual, expected); - } - - template<> template<> - void buffer_object::test<5>() - { - const char ONE[] = "one"; - const char TWO[] = "two"; - std::string expected("netwo"); - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)TWO, 3); - mBuffer.prepend(ch.in(), (U8*)ONE, 3); - char buffer[255]; /* Flawfinder: ignore */ - S32 len = 5; - LLBufferArray::segment_iterator_t it = mBuffer.beginSegment(); - U8* addr = (*it).data(); - mBuffer.readAfter(ch.in(), addr, (U8*)buffer, len); - ensure_equals(len, 5); - buffer[len] = '\0'; - std::string actual(buffer); - ensure_equals("read", actual, expected); - } - - template<> template<> - void buffer_object::test<6>() - { - std::string request("The early bird catches the worm."); - std::string response("If you're a worm, sleep late."); - std::ostringstream expected; - expected << "ContentLength: " << response.length() << "\r\n\r\n" - << response; - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)request.c_str(), request.length()); - mBuffer.append(ch.out(), (U8*)response.c_str(), response.length()); - S32 count = mBuffer.countAfter(ch.out(), NULL); - std::ostringstream header; - header << "ContentLength: " << count << "\r\n\r\n"; - std::string head(header.str()); - mBuffer.prepend(ch.out(), (U8*)head.c_str(), head.length()); - char buffer[1024]; /* Flawfinder: ignore */ - S32 len = response.size() + head.length(); - ensure_equals("same length", len, (S32)expected.str().length()); - mBuffer.readAfter(ch.out(), NULL, (U8*)buffer, len); - buffer[len] = '\0'; - std::string actual(buffer); - ensure_equals("threaded writes", actual, expected.str()); - } - - template<> template<> - void buffer_object::test<7>() - { - const S32 LINE_COUNT = 3; - std::string lines[LINE_COUNT] = - { - std::string("GET /index.htm HTTP/1.0\r\n"), - std::string("User-Agent: Wget/1.9.1\r\n"), - std::string("Host: localhost:8008\r\n") - }; - std::string text; - S32 i; - for(i = 0; i < LINE_COUNT; ++i) - { - text.append(lines[i]); - } - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)text.c_str(), text.length()); - const S32 BUFFER_LEN = 1024; - char buf[BUFFER_LEN]; - S32 len; - U8* last = NULL; - std::string last_line; - for(i = 0; i < LINE_COUNT; ++i) - { - len = BUFFER_LEN; - last = mBuffer.readAfter(ch.in(), last, (U8*)buf, len); - char* newline = strchr((char*)buf, '\n'); - S32 offset = -((len - 1) - (newline - buf)); - ++newline; - *newline = '\0'; - last_line.assign(buf); - std::ostringstream message; - message << "line reads in line[" << i << "]"; - ensure_equals(message.str().c_str(), last_line, lines[i]); - last = mBuffer.seek(ch.in(), last, offset); - } - } - - template<> template<> - void buffer_object::test<8>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)"1", 1); - LLBufferArray buffer; - buffer.append(ch.in(), (U8*)"2", 1); - mBuffer.takeContents(buffer); - mBuffer.append(ch.in(), (U8*)"3", 1); - S32 count = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("buffer size", count, 3); - U8* temp = new U8[count]; - mBuffer.readAfter(ch.in(), NULL, temp, count); - ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3))); - delete[] temp; - } - - template<> template<> - void buffer_object::test<9>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.in(), (U8*)"1", 1); - S32 capacity = mBuffer.capacity(); - ensure("has capacity", capacity > 0); - U8* temp = new U8[capacity - 1]; - mBuffer.append(ch.in(), temp, capacity - 1); - capacity = mBuffer.capacity(); - ensure("has capacity when full", capacity > 0); - S32 used = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("used equals capacity", used, capacity); - - LLBufferArray::segment_iterator_t iter = mBuffer.beginSegment(); - while(iter != mBuffer.endSegment()) - { - mBuffer.eraseSegment(iter++); - } - - used = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("used is zero", used, 0); - S32 capacity2 = mBuffer.capacity(); - ensure_equals("capacity the same after erase", capacity2, capacity); - mBuffer.append(ch.in(), temp, capacity - 1); - capacity2 = mBuffer.capacity(); - ensure_equals("capacity the same after append", capacity2, capacity); - - delete[] temp; - } - -#if 0 - template<> template<> - void buffer_object::test<9>() - { - char buffer[1024]; /* Flawfinder: ignore */ - S32 size = sprintf(buffer, - "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x", - 7, - 7, - "Hang Glider INFO", - "18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a", - "0e346d8b-4433-4d66-a6b0-fd37083abc4c", - "0e346d8b-4433-4d66-a6b0-fd37083abc4c", - "00000000-0000-0000-0000-000000000000", - 0x7fffffff, - 0x7fffffff, - 0, - 0, - 0x7fffffff, - "69e0d357-2e7c-8990-a2bc-7f61c868e5a3", - "2004-06-04 16:09:17 note card", - 0, - 10, - 0) + 1; - - //const char* expected = "7|7|Hang Glider INFO|18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a|0e346d8b-4433-4d66-a6b0-fd37083abc4c|0e346d8b-4433-4d66-a6b0-fd37083abc4c|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|7fffffff|69e0d357-2e7c-8990-a2bc-7f61c868e5a3|2004-06-04 16:09:17 note card|0|10|0\0"; - - LLSD* bin_bucket = LLIMInfo::buildSDfrombuffer((U8*)buffer,size); - - char post_buffer[1024]; - U32 post_size; - LLIMInfo::getBinaryBucket(bin_bucket,(U8*)post_buffer,post_size); - ensure_equals("Buffer sizes",size,(S32)post_size); - ensure("Buffer content",!strcmp(buffer,post_buffer)); - } -#endif - - /* - template<> template<> - void buffer_object::test<>() - { - } - */ -} - -namespace tut -{ - struct buffer_and_stream_data - { - LLBufferArray mBuffer; - }; - typedef test_group bas_test; - typedef bas_test::object bas_object; - tut::bas_test tbs("buffer_stream"); - - template<> template<> - void bas_object::test<1>() - { - const char HELLO_WORLD[] = "hello world"; - const S32 str_len = strlen(HELLO_WORLD); /* Flawfinder: ignore */ - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream str(ch, &mBuffer); - mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len); - std::string hello; - std::string world; - str >> hello >> world; - ensure_equals("first word", hello, std::string("hello")); - ensure_equals("second word", world, std::string("world")); - } - - template<> template<> - void bas_object::test<2>() - { - std::string part1("Eat my shor"); - std::string part2("ts ho"); - std::string part3("mer"); - std::string ignore("ignore me"); - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream str(ch, &mBuffer); - mBuffer.append(ch.in(), (U8*)part1.c_str(), part1.length()); - mBuffer.append(ch.in(), (U8*)part2.c_str(), part2.length()); - mBuffer.append(ch.out(), (U8*)ignore.c_str(), ignore.length()); - mBuffer.append(ch.in(), (U8*)part3.c_str(), part3.length()); - std::string eat; - std::string my; - std::string shorts; - std::string homer; - str >> eat >> my >> shorts >> homer; - ensure_equals("word1", eat, std::string("Eat")); - ensure_equals("word2", my, std::string("my")); - ensure_equals("word3", shorts, std::string("shorts")); - ensure_equals("word4", homer, std::string("homer")); - } - - template<> template<> - void bas_object::test<3>() - { - std::string part1("junk in "); - std::string part2("the trunk"); - const S32 CHANNEL = 0; - mBuffer.append(CHANNEL, (U8*)part1.c_str(), part1.length()); - mBuffer.append(CHANNEL, (U8*)part2.c_str(), part2.length()); - U8* last = 0; - const S32 BUF_LEN = 128; - char buf[BUF_LEN]; - S32 len = 11; - last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len); - buf[len] = '\0'; - std::string actual(buf); - ensure_equals("first read", actual, std::string("junk in the")); - last = mBuffer.seek(CHANNEL, last, -6); - len = 12; - last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len); - buf[len] = '\0'; - actual.assign(buf); - ensure_equals("seek and read", actual, std::string("in the trunk")); - } - - template<> template<> - void bas_object::test<4>() - { - std::string phrase("zippity do da!"); - const S32 CHANNEL = 0; - mBuffer.append(CHANNEL, (U8*)phrase.c_str(), phrase.length()); - const S32 BUF_LEN = 128; - char buf[BUF_LEN]; - S32 len = 7; - U8* last = mBuffer.readAfter(CHANNEL, NULL, (U8*)buf, len); - mBuffer.splitAfter(last); - LLBufferArray::segment_iterator_t it = mBuffer.beginSegment(); - LLBufferArray::segment_iterator_t end = mBuffer.endSegment(); - std::string first((char*)((*it).data()), (*it).size()); - ensure_equals("first part", first, std::string("zippity")); - ++it; - std::string second((char*)((*it).data()), (*it).size()); - ensure_equals("second part", second, std::string(" do da!")); - ++it; - ensure("iterators equal", (it == end)); - } - - template<> template<> - void bas_object::test<5>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream str(ch, &mBuffer); - std::string h1("hello"); - std::string h2(", how are you doing?"); - std::string expected(h1); - expected.append(h2); - str << h1 << h2; - str.flush(); - const S32 BUF_LEN = 128; - char buf[BUF_LEN]; - S32 actual_len = BUF_LEN; - S32 expected_len = h1.size() + h2.size(); - (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len); - ensure_equals("streamed size", actual_len, expected_len); - buf[actual_len] = '\0'; - std::string actual(buf); - ensure_equals("streamed to buf", actual, expected); - } - - template<> template<> - void bas_object::test<6>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream bstr(ch, &mBuffer); - std::ostringstream ostr; - std::vector ids; - LLUUID id; - for(int i = 0; i < 5; ++i) - { - id.generate(); - ids.push_back(id); - } - bstr << "SELECT concat(u.username, ' ', l.name) " - << "FROM user u, user_last_name l " - << "WHERE u.last_name_id = l.last_name_id" - << " AND u.agent_id IN ('"; - ostr << "SELECT concat(u.username, ' ', l.name) " - << "FROM user u, user_last_name l " - << "WHERE u.last_name_id = l.last_name_id" - << " AND u.agent_id IN ('"; - std::copy( - ids.begin(), - ids.end(), - std::ostream_iterator(bstr, "','")); - std::copy( - ids.begin(), - ids.end(), - std::ostream_iterator(ostr, "','")); - bstr.seekp(-2, std::ios::cur); - ostr.seekp(-2, std::ios::cur); - bstr << ") "; - ostr << ") "; - bstr.flush(); - const S32 BUF_LEN = 512; - char buf[BUF_LEN]; /* Flawfinder: ignore */ - S32 actual_len = BUF_LEN; - (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len); - buf[actual_len] = '\0'; - std::string actual(buf); - std::string expected(ostr.str()); - ensure_equals("size of string in seek",actual.size(),expected.size()); - ensure_equals("seek in ostream", actual, expected); - } - - template<> template<> - void bas_object::test<7>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream bstr(ch, &mBuffer); - bstr << "1"; - bstr.flush(); - S32 count = mBuffer.countAfter(ch.out(), NULL); - ensure_equals("buffer size 1", count, 1); - LLBufferArray buffer; - buffer.append(ch.out(), (U8*)"2", 1); - mBuffer.takeContents(buffer); - count = mBuffer.countAfter(ch.out(), NULL); - ensure_equals("buffer size 2", count, 2); - bstr << "3"; - bstr.flush(); - count = mBuffer.countAfter(ch.out(), NULL); - ensure_equals("buffer size 3", count, 3); - U8* temp = new U8[count]; - mBuffer.readAfter(ch.out(), NULL, temp, count); - ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3))); - delete[] temp; - } - - template<> template<> - void bas_object::test<8>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream ostr(ch, &mBuffer); - typedef std::vector buf_t; - typedef std::vector actual_t; - actual_t actual; - buf_t source; - bool need_comma = false; - ostr << "["; - S32 total_size = 1; - for(S32 i = 2000; i < 2003; ++i) - { - if(need_comma) - { - ostr << ","; - ++total_size; - } - need_comma = true; - srand(69 + i); /* Flawfinder: ignore */ - S32 size = rand() % 1000 + 1000; - std::generate_n( - std::back_insert_iterator(source), - size, - rand); - actual.push_back(source); - ostr << "b(" << size << ")\""; - total_size += 8; - ostr.write((const char*)(&source[0]), size); - total_size += size; - source.clear(); - ostr << "\""; - ++total_size; - } - ostr << "]"; - ++total_size; - ostr.flush(); - - // now that we have a bunch of data on a stream, parse it all. - ch = mBuffer.nextChannel(); - S32 count = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("size of buffer", count, total_size); - LLBufferStream istr(ch, &mBuffer); - LLSD data; - count = LLSDSerialize::fromNotation(data, istr, total_size); - ensure("sd parsed", data.isDefined()); - - for(S32 j = 0; j < 3; ++j) - { - std::ostringstream name; - LLSD child(data[j]); - name << "found buffer " << j; - ensure(name.str(), child.isDefined()); - source = child.asBinary(); - name.str(""); - name << "buffer " << j << " size"; - ensure_equals(name.str().c_str(), source.size(), actual[j].size()); - name.str(""); - name << "buffer " << j << " contents"; - ensure( - name.str(), - (0 == memcmp(&source[0], &actual[j][0], source.size()))); - } - } - - template<> template<> - void bas_object::test<9>() - { - LLChannelDescriptors ch = mBuffer.nextChannel(); - LLBufferStream ostr(ch, &mBuffer); - typedef std::vector buf_t; - buf_t source; - bool need_comma = false; - ostr << "{"; - S32 total_size = 1; - for(S32 i = 1000; i < 3000; ++i) - { - if(need_comma) - { - ostr << ","; - ++total_size; - } - need_comma = true; - ostr << "'" << i << "':"; - total_size += 7; - srand(69 + i); /* Flawfinder: ignore */ - S32 size = rand() % 1000 + 1000; - std::generate_n( - std::back_insert_iterator(source), - size, - rand); - ostr << "b(" << size << ")\""; - total_size += 8; - ostr.write((const char*)(&source[0]), size); - total_size += size; - source.clear(); - ostr << "\""; - ++total_size; - } - ostr << "}"; - ++total_size; - ostr.flush(); - - // now that we have a bunch of data on a stream, parse it all. - ch = mBuffer.nextChannel(); - S32 count = mBuffer.countAfter(ch.in(), NULL); - ensure_equals("size of buffer", count, total_size); - LLBufferStream istr(ch, &mBuffer); - LLSD data; - count = LLSDSerialize::fromNotation(data, istr, total_size); - ensure("sd parsed", data.isDefined()); - } - - template<> template<> - void bas_object::test<10>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - const char LOGIN_STREAM[] = "{'method':'login', 'parameter': [ {" - "'uri': 'sl-am:kellys.region.siva.lindenlab.com/location?start=url&px=128&py=128&pz=128&lx=0&ly=0&lz=0'}, " - "{'version': i1}, {'texture_data': [ '61d724fb-ad79-f637-2186-5cf457560daa', '6e38b9be-b7cc-e77a-8aec-029a42b0b416', " - "'a9073524-e89b-2924-ca6e-a81944109a1a', '658f18b5-5f1e-e593-f5d5-36c3abc7249a', '0cc799f4-8c99-6b91-bd75-b179b12429e2', " - "'59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '5748decc-f629-461c-9a36-a35a221fe21f', " - "'b8fc9be2-26a6-6b47-690b-0e902e983484', 'a13ca0fe-3802-dc97-e79a-70d12171c724', 'dd9643cf-fd5d-0376-ed4a-b1cc646a97d5', " - "'4ad13ae9-a112-af09-210a-cf9353a7a9e7', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', " - "'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', " - "'5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97']," - "'session_id': '324cfa9f-fe5d-4d1c-a317-35f20a86a4d1','position': [ i128, i128, i128],'last_name': 'Linden','group_title': '-> !BLING! <-','group_name': 'test!','agent_access': 'M'," - "'attachment_data': [ {'asset_id': 'aaede2b1-9955-09d4-5c93-2b557c778cf3','attachment_point': i6,'item_id': 'f3694abc-5122-db33-73d9-e0f4288dc2bf'}]," - "'buddy_ids': [ '101358d5-469d-4b24-9b85-4dc3c05e635d', '1b00fec7-6265-4875-acac-80d9cfe9295c', '203ad6df-b522-491d-ba48-4e24eb57aeff', " - "'22d4dcdb-aebb-47fa-b925-a871cc75ee48','27da3df5-1339-4463-80aa-40504ee3b3e5', '299d1720-b61f-4268-8c29-9614aa2d44c2', " - "'2b048a24-2737-4994-9fa5-becc8e466253', '2cd5dc14-a853-49a4-be3c-a5a7178e37bc', '3de548e1-57be-cfea-2b78-83ae3ad95998', " - "'3dee98e4-a6a3-4543-91c3-bbd528447ba7', '3e2d81a3-6263-6ffe-ad5c-8ce04bee07e9', '40e70b98-fed7-47f3-9700-1bce93f9350b', " - "'50a9b68e-b5aa-4d35-9137-3cfebda0a15c', '54295571-9357-43ff-ae74-a83b5138160f', '6191e2d7-5f96-4856-bdab-af0f79f47ae4', " - "'63e577d8-cd34-4235-a0a3-de0500133364', '79cfb666-4fd0-4af7-95df-fb7d96b4e24d', '8121c2f3-4a88-4c33-9899-8fc1273f47ee', " - "'909da964-ef23-4f2a-ba13-f2a8cfd454b6','a2e76fcd-9360-4f6d-a924-000000000001', 'aaa6d664-527e-4d83-9cbb-7ef79ccc7cc8', " - "'b79bfb6c-23be-49eb-b35b-30ff2f501b37', 'ba0d9c79-148c-4a79-8e3c-0665eebe2427', 'bc9bda98-57cd-498f-b993-4ff1ac9dec93', " - "'c62d16f6-81cb-419d-9cac-e46dc394084d', 'd48f8fa7-2512-4fe5-80c8-c0a923412e07', 'd77e3e24-7e6c-4c3f-96d0-a1746337f8fb', " - "'da615c63-a84b-4592-a3d6-a90dd3e92e6e', 'df47190a-7eb7-4aff-985f-2d1d3ad6c6e9', 'e3380196-72cd-499c-a2ba-caa180bd5fe4', " - "'e937863f-f134-4207-803b-d6e686651d6c', 'efcdf98b-5269-45ef-ac7a-0671f09ea9d9']," - "'circuit_code': i124,'group_id': '8615c885-9cf0-bf0a-6e40-0c11462aa652','limited_to_estate': i1,'look_at': [ i0, i0, i0]," - "'agent_id': '0e346d8b-4433-4d66-a6b0-fd37083abc4c','first_name': 'Kelly','start': 'url'}]}"; - LLChannelDescriptors ch = mBuffer.nextChannel(); - mBuffer.append(ch.out(), (U8*)LOGIN_STREAM, strlen(LOGIN_STREAM)); /* Flawfinder: ignore */ - ch = mBuffer.nextChannel(); - LLBufferStream istr(ch, &mBuffer); - LLSD data; - S32 count = LLSDSerialize::fromNotation( - data, - istr, - mBuffer.count(ch.in())); - ensure("parsed something", (count > 0)); - ensure("sd parsed", data.isDefined()); - ensure_equals("sd type", data.type(), LLSD::TypeMap); - ensure("has method", data.has("method")); - ensure("has parameter", data.has("parameter")); - LLSD parameter = data["parameter"]; - ensure_equals("parameter is array", parameter.type(), LLSD::TypeArray); - LLSD agent_params = parameter[2]; - std::string s_value; - s_value = agent_params["last_name"].asString(); - ensure_equals("last name", s_value, std::string("Linden")); - s_value = agent_params["first_name"].asString(); - ensure_equals("first name", s_value, std::string("Kelly")); - s_value = agent_params["agent_access"].asString(); - ensure_equals("agent access", s_value, std::string("M")); - s_value = agent_params["group_name"].asString(); - ensure_equals("group name", s_value, std::string("test!")); - s_value = agent_params["group_title"].asString(); - ensure_equals("group title", s_value, std::string("-> !BLING! <-")); - - LLUUID agent_id("0e346d8b-4433-4d66-a6b0-fd37083abc4c"); - LLUUID id = agent_params["agent_id"]; - ensure_equals("agent id", id, agent_id); - LLUUID session_id("324cfa9f-fe5d-4d1c-a317-35f20a86a4d1"); - id = agent_params["session_id"]; - ensure_equals("session id", id, session_id); - LLUUID group_id ("8615c885-9cf0-bf0a-6e40-0c11462aa652"); - id = agent_params["group_id"]; - ensure_equals("group id", id, group_id); - - S32 i_val = agent_params["limited_to_estate"]; - ensure_equals("limited to estate", i_val, 1); - i_val = agent_params["circuit_code"]; - ensure_equals("circuit code", i_val, 124); - } - - - template<> template<> - void bas_object::test<11>() - { - std::string val = "{!'foo'@:#'bar'}"; - std::istringstream istr; - istr.str(val); - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parser error return value", count, -1); - ensure("data undefined", sd.isUndefined()); - } - - template<> template<> - void bas_object::test<12>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string val = "{!'foo':[i1,'hi',{@'bar'#:[$i2%,^'baz'&]*}+]=}"; - std::istringstream istr; - istr.str(val); - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parser error return value", count, -1); - ensure("data undefined", sd.isUndefined()); - } - -/* - template<> template<> - void bas_object::test<13>() - { - } - template<> template<> - void bas_object::test<14>() - { - } - template<> template<> - void bas_object::test<15>() - { - } -*/ -} - - -namespace tut -{ - class PumpAndChainTestData - { - protected: - apr_pool_t* mPool; - LLPumpIO* mPump; - LLPumpIO::chain_t mChain; - - public: - PumpAndChainTestData() - { - apr_pool_create(&mPool, NULL); - mPump = new LLPumpIO(mPool); - } - - ~PumpAndChainTestData() - { - mChain.clear(); - delete mPump; - apr_pool_destroy(mPool); - } - }; - typedef test_group PumpAndChainTestGroup; - typedef PumpAndChainTestGroup::object PumpAndChainTestObject; - PumpAndChainTestGroup pumpAndChainTestGroup("pump_and_chain"); - - template<> template<> - void PumpAndChainTestObject::test<1>() - { - LLPipeStringExtractor* extractor = new LLPipeStringExtractor(); - - mChain.push_back(LLIOPipe::ptr_t(new LLIOFlush)); - mChain.push_back(LLIOPipe::ptr_t(extractor)); - - LLTimer timer; - timer.setTimerExpirySec(100.0f); - - mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS); - while(!extractor->done() && !timer.hasExpired()) - { - mPump->pump(); - mPump->callback(); - } - - ensure("reading string finished", extractor->done()); - ensure_equals("string was empty", extractor->string(), ""); - } -} - -/* -namespace tut -{ - struct double_construct - { - public: - double_construct() - { - llinfos << "constructed" << llendl; - } - ~double_construct() - { - llinfos << "destroyed" << llendl; - } - }; - typedef test_group double_construct_test_group; - typedef double_construct_test_group::object dc_test_object; - double_construct_test_group dctest("double construct"); - template<> template<> - void dc_test_object::test<1>() - { - ensure("test 1", true); - } -} -*/ - -namespace tut -{ - /** - * @brief we want to test the pipes & pumps under bad conditions. - */ - struct pipe_and_pump_fitness - { - public: - enum - { - SERVER_LISTEN_PORT = 13050 - }; - - pipe_and_pump_fitness() - { - LLFrameTimer::updateFrameTime(); - apr_pool_create(&mPool, NULL); - mPump = new LLPumpIO(mPool); - mSocket = LLSocket::create( - mPool, - LLSocket::STREAM_TCP, - SERVER_LISTEN_PORT); - } - - ~pipe_and_pump_fitness() - { - mSocket.reset(); - delete mPump; - apr_pool_destroy(mPool); - } - - protected: - apr_pool_t* mPool; - LLPumpIO* mPump; - LLSocket::ptr_t mSocket; - }; - typedef test_group fitness_test_group; - typedef fitness_test_group::object fitness_test_object; - fitness_test_group fitness("pipe and pump fitness"); - - template<> template<> - void fitness_test_object::test<1>() - { - lldebugs << "fitness_test_object::test<1>()" << llendl; - - // Set up the server - //lldebugs << "fitness_test_object::test<1> - setting up server." - // << llendl; - LLPumpIO::chain_t chain; - typedef LLCloneIOFactory emitter_t; - emitter_t* emitter = new emitter_t( - new LLPipeStringInjector("suckers never play me")); - boost::shared_ptr factory(emitter); - LLIOServerSocket* server = new LLIOServerSocket( - mPool, - mSocket, - factory); - server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS); - chain.push_back(LLIOPipe::ptr_t(server)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - - // We need to tickle the pump a little to set up the listen() - //lldebugs << "fitness_test_object::test<1> - initializing server." - // << llendl; - pump_loop(mPump, 0.1f); - - // Set up the client - //lldebugs << "fitness_test_object::test<1> - connecting client." - // << llendl; - LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); - bool connected = client->blockingConnect(server_host); - ensure("Connected to server", connected); - lldebugs << "connected" << llendl; - - // We have connected, since the socket reader does not block, - // the first call to read data will return EAGAIN, so we need - // to write something. - chain.clear(); - chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); - chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); - chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, 1.0f); - - // Now, the server should immediately send the data, but we'll - // never read it. pump for a bit - F32 elapsed = pump_loop(mPump, 2.0f); - ensure("Did not take too long", (elapsed < 3.0f)); - } - - template<> template<> - void fitness_test_object::test<2>() - { - lldebugs << "fitness_test_object::test<2>()" << llendl; - - // Set up the server - LLPumpIO::chain_t chain; - typedef LLCloneIOFactory emitter_t; - emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000)); - boost::shared_ptr factory(emitter); - LLIOServerSocket* server = new LLIOServerSocket( - mPool, - mSocket, - factory); - server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS); - chain.push_back(LLIOPipe::ptr_t(server)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - - // We need to tickle the pump a little to set up the listen() - pump_loop(mPump, 0.1f); - - // Set up the client - LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); - bool connected = client->blockingConnect(server_host); - ensure("Connected to server", connected); - lldebugs << "connected" << llendl; - - // We have connected, since the socket reader does not block, - // the first call to read data will return EAGAIN, so we need - // to write something. - chain.clear(); - chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); - chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); - chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS / 2.0f); - - // Now, the server should immediately send the data, but we'll - // never read it. pump for a bit - F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f); - ensure("Did not take too long", (elapsed < 3.0f)); - } - - template<> template<> - void fitness_test_object::test<3>() - { - lldebugs << "fitness_test_object::test<3>()" << llendl; - - // Set up the server - LLPumpIO::chain_t chain; - typedef LLCloneIOFactory emitter_t; - emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000)); - boost::shared_ptr factory(emitter); - LLIOServerSocket* server = new LLIOServerSocket( - mPool, - mSocket, - factory); - server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS); - chain.push_back(LLIOPipe::ptr_t(server)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - - // We need to tickle the pump a little to set up the listen() - pump_loop(mPump, 0.1f); - - // Set up the client - LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); - bool connected = client->blockingConnect(server_host); - ensure("Connected to server", connected); - lldebugs << "connected" << llendl; - - // We have connected, since the socket reader does not block, - // the first call to read data will return EAGAIN, so we need - // to write something. - chain.clear(); - chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); - chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); - chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS * 2.0f); - - // Now, the server should immediately send the data, but we'll - // never read it. pump for a bit - F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f + 1.0f); - ensure("Did not take too long", (elapsed < 4.0f)); - } - - template<> template<> - void fitness_test_object::test<4>() - { - lldebugs << "fitness_test_object::test<4>()" << llendl; - - // Set up the server - LLPumpIO::chain_t chain; - typedef LLCloneIOFactory emitter_t; - emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000)); - boost::shared_ptr factory(emitter); - LLIOServerSocket* server = new LLIOServerSocket( - mPool, - mSocket, - factory); - server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS + 1.80f); - chain.push_back(LLIOPipe::ptr_t(server)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - - // We need to tickle the pump a little to set up the listen() - pump_loop(mPump, 0.1f); - - // Set up the client - LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); - bool connected = client->blockingConnect(server_host); - ensure("Connected to server", connected); - lldebugs << "connected" << llendl; - - // We have connected, since the socket reader does not block, - // the first call to read data will return EAGAIN, so we need - // to write something. - chain.clear(); - chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); - chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); - chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - - // Now, the server should immediately send the data, but we'll - // never read it. pump for a bit - F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS + 3.0f); - ensure("Did not take too long", (elapsed < DEFAULT_CHAIN_EXPIRY_SECS)); - } - - template<> template<> - void fitness_test_object::test<5>() - { - // Set up the server - LLPumpIO::chain_t chain; - typedef LLCloneIOFactory sleeper_t; - sleeper_t* sleeper = new sleeper_t(new LLIOSleeper); - boost::shared_ptr factory(sleeper); - LLIOServerSocket* server = new LLIOServerSocket( - mPool, - mSocket, - factory); - server->setResponseTimeout(1.0); - chain.push_back(LLIOPipe::ptr_t(server)); - mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS); - // We need to tickle the pump a little to set up the listen() - pump_loop(mPump, 0.1f); - U32 count = mPump->runningChains(); - ensure_equals("server chain onboard", count, 1); - lldebugs << "** Server is up." << llendl; - - // Set up the client - LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP); - LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT); - bool connected = client->blockingConnect(server_host); - ensure("Connected to server", connected); - lldebugs << "connected" << llendl; - F32 elapsed = pump_loop(mPump,0.1f); - count = mPump->runningChains(); - ensure_equals("server chain onboard", count, 2); - lldebugs << "** Client is connected." << llendl; - - // We have connected, since the socket reader does not block, - // the first call to read data will return EAGAIN, so we need - // to write something. - chain.clear(); - chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi"))); - chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client))); - chain.push_back(LLIOPipe::ptr_t(new LLIONull)); - mPump->addChain(chain, 0.2f); - chain.clear(); - - // pump for a bit and make sure all 3 chains are running - elapsed = pump_loop(mPump,0.1f); - count = mPump->runningChains(); - ensure_equals("client chain onboard", count, 3); - lldebugs << "** request should have been sent." << llendl; - - // pump for long enough the the client socket closes, and the - // server socket should not be closed yet. - elapsed = pump_loop(mPump,0.2f); - count = mPump->runningChains(); - ensure_equals("client chain timed out ", count, 2); - lldebugs << "** client chain should be closed." << llendl; - - // At this point, the socket should be closed by the timeout - elapsed = pump_loop(mPump,1.0f); - count = mPump->runningChains(); - ensure_equals("accepted socked close", count, 1); - lldebugs << "** Sleeper should have timed out.." << llendl; - } -} - -namespace tut -{ - struct rpc_server_data - { - class LLSimpleRPCResponse : public LLSDRPCResponse - { - public: - LLSimpleRPCResponse(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCResponse() {} - virtual bool response(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return true; - } - virtual bool fault(LLPumpIO* pump) - { - *mResponsePtr = mReturnValue; - return false; - } - virtual bool error(LLPumpIO* pump) - { - ensure("LLSimpleRPCResponse::error()", false); - return false; - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCClient : public LLSDRPCClient - { - public: - LLSimpleRPCClient(LLSD* response) : - mResponsePtr(response) - { - } - ~LLSimpleRPCClient() {} - void echo(const LLSD& parameter) - { - LLSimpleRPCResponse* resp; - resp = new LLSimpleRPCResponse(mResponsePtr); - static const std::string URI_NONE; - static const std::string METHOD_ECHO("echo"); - call(URI_NONE, METHOD_ECHO, parameter, resp, EPBQ_CALLBACK); - } - public: - LLSD* mResponsePtr; - }; - - class LLSimpleRPCServer : public LLSDRPCServer - { - public: - LLSimpleRPCServer() - { - mMethods["echo"] = new mem_fn_t( - this, - &LLSimpleRPCServer::rpc_Echo); - } - ~LLSimpleRPCServer() {} - protected: - typedef LLSDRPCMethodCall mem_fn_t; - ESDRPCSStatus rpc_Echo( - const LLSD& parameter, - const LLChannelDescriptors& channels, - LLBufferArray* data) - { - buildResponse(channels, data, parameter); - return ESDRPCS_DONE; - } - }; - - apr_pool_t* mPool; - LLPumpIO* mPump; - LLPumpIO::chain_t mChain; - LLSimpleRPCClient* mClient; - LLSD mResponse; - - rpc_server_data() : - mPool(NULL), - mPump(NULL), - mClient(NULL) - { - apr_pool_create(&mPool, NULL); - mPump = new LLPumpIO(mPool); - mClient = new LLSimpleRPCClient(&mResponse); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(new LLSimpleRPCServer)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse)); - mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD)); - mChain.push_back(LLIOPipe::ptr_t(mClient)); - } - ~rpc_server_data() - { - mChain.clear(); - delete mPump; - mPump = NULL; - apr_pool_destroy(mPool); - mPool = NULL; - } - void pump_loop(const LLSD& request) - { - LLTimer timer; - timer.setTimerExpirySec(1.0f); - mClient->echo(request); - mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS); - while(mResponse.isUndefined() && !timer.hasExpired()) - { - mPump->pump(); - mPump->callback(); - } - } - }; - typedef test_group rpc_server_test; - typedef rpc_server_test::object rpc_server_object; - tut::rpc_server_test rpc("rpc_server"); - - template<> template<> - void rpc_server_object::test<1>() - { - LLSD request; - request = 1; - pump_loop(request); - //llinfos << "request: " << *request << llendl; - //llinfos << "response: " << *mResponse << llendl; - ensure_equals("integer request response", mResponse.asInteger(), 1); - } - - template<> template<> - void rpc_server_object::test<2>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - std::stringstream stream; - stream << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector expected_binary; - expected_binary.resize(stream.str().size()); - memcpy(&expected_binary[0], stream.str().c_str(), stream.str().size()); /* Flawfinder: ignore */ - stream.str(""); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'}," - << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << expected_binary.size() << ")\""; - stream.write((const char*)&expected_binary[0], expected_binary.size()); - stream << "\"}" - << "]" - << "}]"; - - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - stream, - stream.str().size()); - ensure("parsed something", (count > 0)); - - pump_loop(request); - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - ensure_equals( - "uri parameter type", - mResponse[0].type(), - LLSD::TypeMap); - ensure_equals( - "uri type", - mResponse[0]["uri"].type(), - LLSD::TypeString); - ensure_equals("uri value", mResponse[0]["uri"].asString(), uri); - - ensure_equals( - "version parameter type", - mResponse[1].type(), - LLSD::TypeMap); - ensure_equals( - "version type", - mResponse[1]["version"].type(), - LLSD::TypeInteger); - ensure_equals( - "version value", - mResponse[1]["version"].asInteger(), - 1); - - ensure_equals("agent params type", mResponse[2].type(), LLSD::TypeMap); - LLSD attachment_data = mResponse[2]["attachment_data"]; - ensure("attachment data exists", attachment_data.isDefined()); - ensure_equals( - "attachment type", - attachment_data.type(), - LLSD::TypeArray); - ensure_equals( - "attachment type 0", - attachment_data[0].type(), - LLSD::TypeMap); - ensure_equals( - "attachment type 1", - attachment_data[1].type(), - LLSD::TypeMap); - ensure_equals("attachment size 1", attachment_data[1].size(), 3); - ensure_equals( - "asset data type", - attachment_data[1]["asset_data"].type(), - LLSD::TypeBinary); - std::vector actual_binary; - actual_binary = attachment_data[1]["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<3>() - { -//#if LL_WINDOWS && _MSC_VER >= 1400 -// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); -//#endif - std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0"); - - LLBufferArray buffer; - LLChannelDescriptors buffer_channels = buffer.nextChannel(); - LLBufferStream stream(buffer_channels, &buffer); - stream << "[{'uri':'" << uri << "'}, {'version':i1}, " - << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], " - << "'attachment_data':[" - << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},"; - - std::stringstream tmp_str; - tmp_str << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}"; - std::vector expected_binary; - expected_binary.resize(tmp_str.str().size()); - memcpy( /* Flawfinder: ignore */ - &expected_binary[0], - tmp_str.str().c_str(), - tmp_str.str().size()); - - LLBufferArray attachment_buffer; - LLChannelDescriptors attach_channels = attachment_buffer.nextChannel(); - LLBufferStream attach_stream(attach_channels, &attachment_buffer); - attach_stream.write((const char*)&expected_binary[0], expected_binary.size()); - attach_stream.flush(); - S32 len = attachment_buffer.countAfter(attach_channels.out(), NULL); - stream << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << len << ")\""; - stream.flush(); - buffer.takeContents(attachment_buffer); - stream << "\"}]}]"; - stream.flush(); - - LLChannelDescriptors read_channel = buffer.nextChannel(); - LLBufferStream read_stream(read_channel, &buffer); - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - read_stream, - buffer.count(read_channel.in())); - ensure("parsed something", (count > 0)); - ensure("deserialized", request.isDefined()); - - // do the rpc round trip - pump_loop(request); - - ensure_equals("return type", mResponse.type(), LLSD::TypeArray); - ensure_equals("return size", mResponse.size(), 3); - - LLSD child = mResponse[0]; - ensure("uri map exists", child.isDefined()); - ensure_equals("uri parameter type", child.type(), LLSD::TypeMap); - ensure("uri string exists", child.has("uri")); - ensure_equals("uri type", child["uri"].type(), LLSD::TypeString); - ensure_equals("uri value", child["uri"].asString(), uri); - - child = mResponse[1]; - ensure("version map exists", child.isDefined()); - ensure_equals("version param type", child.type(), LLSD::TypeMap); - ensure_equals( - "version type", - child["version"].type(), - LLSD::TypeInteger); - ensure_equals("version value", child["version"].asInteger(), 1); - - child = mResponse[2]; - ensure("agent params map exists", child.isDefined()); - ensure_equals("agent params type", child.type(), LLSD::TypeMap); - child = child["attachment_data"]; - ensure("attachment data exists", child.isDefined()); - ensure_equals("attachment type", child.type(), LLSD::TypeArray); - LLSD attachment = child[0]; - ensure_equals("attachment type 0", attachment.type(), LLSD::TypeMap); - attachment = child[1]; - ensure_equals("attachment type 1", attachment.type(), LLSD::TypeMap); - ensure_equals("attachment size 1", attachment.size(), 3); - ensure_equals( - "asset data type", - attachment["asset_data"].type(), - LLSD::TypeBinary); - std::vector actual_binary = attachment["asset_data"].asBinary(); - ensure_equals( - "binary data size", - actual_binary.size(), - expected_binary.size()); - ensure( - "binary data", - (0 == memcmp( - &actual_binary[0], - &expected_binary[0], - expected_binary.size()))); - } - - template<> template<> - void rpc_server_object::test<4>() - { - std::string message("parcel '' is naughty."); - std::stringstream str; - str << "{'message':'" << LLSDNotationFormatter::escapeString(message) - << "'}"; - LLSD request; - S32 count = LLSDSerialize::fromNotation( - request, - str, - str.str().size()); - ensure_equals("parse count", count, 2); - ensure_equals("request type", request.type(), LLSD::TypeMap); - pump_loop(request); - ensure("valid response", mResponse.isDefined()); - ensure_equals("response type", mResponse.type(), LLSD::TypeMap); - std::string actual = mResponse["message"].asString(); - ensure_equals("message contents", actual, message); - } - - template<> template<> - void rpc_server_object::test<5>() - { - // test some of the problem cases with llsdrpc over xmlrpc - - // for example: - // * arrays are auto-converted to parameter lists, thus, this - // becomes one parameter. - // * undef goes over the wire as false (this might not be a good idea) - // * uuids are converted to string. - std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; - std::istringstream istr; - istr.str(val); - LLSD sd; - LLSDSerialize::fromNotation(sd, istr, val.size()); - pump_loop(sd); - ensure("valid response", mResponse.isDefined()); - ensure_equals("parsed type", mResponse.type(), LLSD::TypeMap); - ensure_equals("parsed size", mResponse.size(), 2); - LLSD failures = mResponse["failures"]; - ensure_equals("no failures.", failures.asBoolean(), false); - LLSD success = mResponse["successfuls"]; - ensure_equals("success type", success.type(), LLSD::TypeArray); - ensure_equals("success size", success.size(), 1); - ensure_equals( - "success instance type", - success[0].type(), - LLSD::TypeString); - } - -/* - template<> template<> - void rpc_server_object::test<5>() - { - std::string expected("\xf3");//\xffsomething"); - LLSD* request = LLSD::createString(expected); - pump_loop(request); - std::string actual; - mResponse->getString(actual); - if(actual != expected) - { - //llwarns << "iteration " << i << llendl; - std::ostringstream e_str; - std::string::iterator iter = expected.begin(); - std::string::iterator end = expected.end(); - for(; iter != end; ++iter) - { - e_str << (S32)((U8)(*iter)) << " "; - } - e_str << std::endl; - llsd_serialize_string(e_str, expected); - llwarns << "expected size: " << expected.size() << llendl; - llwarns << "expected: " << e_str.str() << llendl; - - std::ostringstream a_str; - iter = actual.begin(); - end = actual.end(); - for(; iter != end; ++iter) - { - a_str << (S32)((U8)(*iter)) << " "; - } - a_str << std::endl; - llsd_serialize_string(a_str, actual); - llwarns << "actual size: " << actual.size() << llendl; - llwarns << "actual: " << a_str.str() << llendl; - } - ensure_equals("binary string request response", actual, expected); - delete request; - } - - template<> template<> - void rpc_server_object::test<5>() - { - } -*/ -} - - -/* -'asset_data':b(12100)"{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemID STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n" -*/ From fe38f59bbb6b8f80b7dc6b2c47ac662bd8bfff62 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 25 Jul 2012 21:26:46 +0200 Subject: [PATCH 045/123] Bug fix --- indra/llmessage/aicurl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 8d696acba..61ed1adb9 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1118,7 +1118,7 @@ CurlResponderBuffer::CurlResponderBuffer() curl_easy_request_w->send_events_to(this); } -#define llmaybeerrs lllog(LLApp::isRunning ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false) +#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false) // The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). // The AIThreadSafeSimple is destructed first (right to left), so when we get here then the From 76eef6fe59438ae8317a3955a73cc076f5d9a9c1 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 29 Jul 2012 18:38:59 +0200 Subject: [PATCH 046/123] Avoid crash on exit. --- indra/llmessage/aicurl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 61ed1adb9..1d52afce9 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1138,7 +1138,7 @@ CurlResponderBuffer::~CurlResponderBuffer() // in which case AICurlEasyRequestStateMachine::mTimer times out, but that already // calls CurlResponderBuffer::timed_out(). llmaybeerrs << "Calling ~CurlResponderBuffer() with active responder!" << llendl; - if (LLApp::isExiting()) + if (!LLApp::isRunning()) { // It might happen if some CurlResponderBuffer escaped clean up somehow :/ mResponder = NULL; From ba65f29a7238b152b58a4528a8a5a31e72aa0106 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 29 Jul 2012 21:12:45 +0200 Subject: [PATCH 047/123] Fix draining of wake up pipe. This is a bug fix, although not one we'd ever run into as normally there is always just one byte to read, never an EAGAIN and certainly never more than 256 bytes. Anyway, also those cases should work now. --- indra/llmessage/aicurlthread.cpp | 57 ++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 2bb33e1a5..c75912047 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -845,27 +845,50 @@ void AICurlThread::wakeup(AICurlMultiHandle_wat const& multi_handle_w) // If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file. // If some process has the pipe open for writing and O_NONBLOCK is set, read() shall return -1 and set errno to [EAGAIN]. char buf[256]; - ssize_t len; - do + bool got_data = false; + for(;;) { - len = read(mWakeUpFd, buf, sizeof(buf)); - if (len == -1 && errno == EAGAIN) + ssize_t len = read(mWakeUpFd, buf, sizeof(buf)); + if (len > 0) + { + // Data was read from the pipe. + got_data = true; + if (len < sizeof(buf)) + break; + } + else if (len == -1) + { + // An error occurred. + if (errno == EAGAIN) + { + if (got_data) + break; + // There was no data, even though select() said so. If this ever happens at all(?), lets just return and enter select() again. + return; + } + else if (errno == EINTR) + { + continue; + } + else + { + llerrs << "read(3) from mWakeUpFd: " << strerror(errno) << llendl; + return; + } + } + else + { + // pipe(2) returned 0. + llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; + close(mWakeUpFd); + mWakeUpFd = CURL_SOCKET_BAD; + mRunning = false; return; - } - while(len == -1 && errno == EINTR); - if (len == -1) - { - llerrs << "read(3) from mWakeUpFd: " << strerror(errno) << llendl; - } - if (LL_UNLIKELY(len == 0)) - { - llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; - close(mWakeUpFd); - mWakeUpFd = CURL_SOCKET_BAD; - mRunning = false; - return; + } } #endif + // Data was received on mWakeUpFd. This means that the main-thread added one + // or more commands to the command queue and called wakeUpCurlThread(). process_commands(multi_handle_w); } From f8c2087d740b24100e39176074f02e07a314a406 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sun, 29 Jul 2012 22:47:19 +0200 Subject: [PATCH 048/123] Preliminary Windows support for curlthreading --- indra/llmessage/aicurl.cpp | 6 +- indra/llmessage/aicurlthread.cpp | 108 +++++++++++++++++++++++++++++-- indra/newview/llcurlrequest.cpp | 2 +- 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 1d52afce9..9dda7bf5f 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -134,10 +134,10 @@ static unsigned long __cdecl apr_os_thread_current_wrapper() // OpenSSL uniq id function. void ssl_id_function(CRYPTO_THREADID* thread_id) { -#if 1 // apr_os_thread_current() returns an unsigned long. - CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); -#else // if it would return a pointer. +#if LL_WINDOWS // apr_os_thread_current() returns an unsigned long. CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); +#else // if it would return a pointer. + CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); #endif } #endif // HAVE_CRYPTO_THREADID diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index c75912047..deea1b404 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -727,7 +727,10 @@ class AICurlThread : public LLThread void create_wakeup_fds(void); void cleanup_wakeup_fds(void); +#if (!LL_WINDOWS) + //On Windows, single socket is used for communicating with itself! -SG curl_socket_t mWakeUpFd_in; +#endif curl_socket_t mWakeUpFd; int mZeroTimeOut; @@ -740,7 +743,10 @@ AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), - mWakeUpFd_in(CURL_SOCKET_BAD), mWakeUpFd(CURL_SOCKET_BAD), +#if (!LL_WINDOWS) + mWakeUpFd_in(CURL_SOCKET_BAD), +#endif + mWakeUpFd(CURL_SOCKET_BAD), mZeroTimeOut(0), mRunning(true), mWakeUpFlag(false) { create_wakeup_fds(); @@ -754,12 +760,65 @@ AICurlThread::~AICurlThread() cleanup_wakeup_fds(); } +#if LL_WINDOWS +static std::string formatWSAError() +{ + std::ostringstream r; + int e = WSAGetLastError(); + LPTSTR error_str = 0; + r << e; + if(FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, e, 0, (LPTSTR)&error_str, 0, NULL)) + { + r << " " << utf16str_to_utf8str(error_str); + LocalFree(error_str); + } + else + { + r << " Unknown WinSock error"; + } + return r.str(); +} +#endif + // MAIN-THREAD void AICurlThread::create_wakeup_fds(void) { #if LL_WINDOWS - // Probably need to use sockets here, cause Windows select doesn't work for a pipe. - #error Missing implementation + mWakeUpFd = socket(AF_INET, SOCK_DGRAM, 0); //Maybe IPPROTO_UDP as last argument? -SG + if(mWakeUpFd == INVALID_SOCKET) + { + llerrs << "Failed to create wake-up socket: " << formatWSAError() << llendl; + } + int error; + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int addrlen = sizeof(addr); + error = bind(mWakeUpFd, (sockaddr*) &addr, addrlen); + if(error) + { + llerrs << "Failed to bind wake-up socket: " << formatWSAError() << llendl; + } + error = getsockname(mWakeUpFd, (sockaddr*) &addr, &addrlen); + if(error) + { + llerrs << "Failed to detect wake-up socket: " << formatWSAError() << llendl; + } + error = connect(mWakeUpFd, (sockaddr*) &addr, addrlen); + if(error) + { + llerrs << "Failed to connect wake-up socket: " < Date: Sun, 29 Jul 2012 22:52:20 +0200 Subject: [PATCH 049/123] Use LLBufferArray instead of std::stringstream for CurlResponderBuffer::mInput Note the changed code is never used yet. CurlResponderBuffer::curlReadCallback is only called when Request::post is being used, which is never used. --- indra/llmessage/aicurl.cpp | 20 ++++++++------------ indra/llmessage/aicurlprivate.h | 11 ++++++----- indra/newview/llcurlrequest.cpp | 14 ++++++++++---- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 1d52afce9..13d69e830 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1164,9 +1164,7 @@ void CurlResponderBuffer::resetState(AICurlEasyRequest_wat& curl_easy_request_w) curl_easy_request_w->resetState(); mOutput.reset(); - - mInput.str(""); - mInput.clear(); + mInput.reset(); mHeaderOutput.str(""); mHeaderOutput.clear(); @@ -1184,6 +1182,10 @@ void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); } + mInput.reset(new LLBufferArray); + mInput->setThreaded(true); + mLastRead = NULL; + mOutput.reset(new LLBufferArray); mOutput->setThreaded(true); @@ -1244,16 +1246,10 @@ size_t CurlResponderBuffer::curlReadCallback(char* data, size_t size, size_t nme // to make sure that callbacks and destruction aren't done simultaneously. AICurlEasyRequest_wat buffered_easy_request_w(*lockobj); + S32 bytes = size * nmemb; // The maximum amount to read. AICurlResponderBuffer_wat buffer_w(*lockobj); - S32 n = size * nmemb; - S32 startpos = buffer_w->getInput().tellg(); - buffer_w->getInput().seekg(0, std::ios::end); - S32 endpos = buffer_w->getInput().tellg(); - buffer_w->getInput().seekg(startpos, std::ios::beg); - S32 maxn = endpos - startpos; - n = llmin(n, maxn); - buffer_w->getInput().read(data, n); - return n; + buffer_w->mLastRead = buffer_w->getInput()->readAfter(sChannels.out(), buffer_w->mLastRead, (U8*)data, bytes); + return bytes; // Return the amount actually read. } //static diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 38ce00b5c..14a8b0fa2 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -291,9 +291,9 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents { void resetState(AICurlEasyRequest_wat& curl_easy_request_w); void prepRequest(AICurlEasyRequest_wat& buffered_curl_easy_request_w, std::vector const& headers, AICurlInterface::ResponderPtr responder, S32 time_out = 0, bool post = false); - std::stringstream& getInput() { return mInput; } - std::stringstream& getHeaderOutput() { return mHeaderOutput; } - LLIOPipe::buffer_ptr_t& getOutput() { return mOutput; } + LLIOPipe::buffer_ptr_t& getInput(void) { return mInput; } + std::stringstream& getHeaderOutput(void) { return mHeaderOutput; } + LLIOPipe::buffer_ptr_t& getOutput(void) { return mOutput; } // Called if libcurl doesn't deliver within CurlRequestTimeOut seconds. void timed_out(void); @@ -307,13 +307,14 @@ class CurlResponderBuffer : protected AICurlEasyHandleEvents { /*virtual*/ void removed_from_multi_handle(AICurlEasyRequest_wat& curl_easy_request_w); private: - std::stringstream mInput; + LLIOPipe::buffer_ptr_t mInput; + U8* mLastRead; // Pointer into mInput where we last stopped reading (or NULL to start at the beginning). std::stringstream mHeaderOutput; LLIOPipe::buffer_ptr_t mOutput; AICurlInterface::ResponderPtr mResponder; public: - static LLChannelDescriptors const sChannels; // Channel object for mOutput: we ONLY use channel 0, so this can be a constant. + static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()). private: // This class may only be created by constructing a ThreadSafeBufferedCurlEasyRequest. diff --git a/indra/newview/llcurlrequest.cpp b/indra/newview/llcurlrequest.cpp index 75706af6d..01eb235a3 100644 --- a/indra/newview/llcurlrequest.cpp +++ b/indra/newview/llcurlrequest.cpp @@ -41,6 +41,8 @@ #include "llsdserialize.h" #include "llcurlrequest.h" +#include "llbuffer.h" +#include "llbufferstream.h" #include "statemachine/aicurleasyrequeststatemachine.h" //----------------------------------------------------------------------------- @@ -94,8 +96,11 @@ bool Request::post(std::string const& url, headers_t const& headers, std::string buffer_w->prepRequest(buffered_easy_request_w, headers, responder); - buffer_w->getInput().write(data.data(), data.size()); - S32 bytes = buffer_w->getInput().str().length(); + U32 bytes = data.size(); + bool success = buffer_w->getInput()->append(buffer_w->sChannels.out(), (U8 const*)data.data(), bytes); + llassert_always(success); // AIFIXME: Maybe throw an error. + if (!success) + return false; buffered_easy_request_w->setPost(NULL, bytes); buffered_easy_request_w->addHeader("Content-Type: application/octet-stream"); buffered_easy_request_w->finalizeRequest(url); @@ -121,8 +126,9 @@ bool Request::post(std::string const& url, headers_t const& headers, LLSD const& buffer_w->prepRequest(buffered_easy_request_w, headers, responder); - LLSDSerialize::toXML(data, buffer_w->getInput()); - S32 bytes = buffer_w->getInput().str().length(); + LLBufferStream buffer_stream(buffer_w->sChannels, buffer_w->getInput().get()); + LLSDSerialize::toXML(data, buffer_stream); + S32 bytes = buffer_w->getInput()->countAfter(buffer_w->sChannels.out(), NULL); buffered_easy_request_w->setPost(NULL, bytes); buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml"); buffered_easy_request_w->finalizeRequest(url); From 8945fe4857c78444bd3742a700bb77d803c70c3c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 30 Jul 2012 01:47:27 +0200 Subject: [PATCH 050/123] Unbreak standalone install on 64bit linux --- indra/newview/viewer_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0c06505e4..5e85cdadc 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -858,7 +858,7 @@ class Linux_x86_64Manifest(LinuxManifest): self.end_prefix("lib32") # 32bit libs needed for voice - if self.prefix("../../libraries/x86_64-linux/lib/release/32bit-compat", dst="lib32"): + if self.prefix("../../libraries/x86_64-linux/lib_release_client/32bit-compat", dst="lib32"): self.path("libalut.so") self.path("libidn.so.11") self.path("libopenal.so.1") From d19c5a4a4a35a0049a2042855280ed7bdfc0bf6d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 30 Jul 2012 02:44:51 +0200 Subject: [PATCH 051/123] Move apr_os_thread_current_wrapper closer to where it's used, and fix a comment. --- indra/llmessage/aicurl.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 9dda7bf5f..d079711d1 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -123,20 +123,13 @@ void ssl_locking_function(int mode, int n, char const* file, int line) } } -#if LL_WINDOWS -static unsigned long __cdecl apr_os_thread_current_wrapper() -{ - return (unsigned long)apr_os_thread_current(); -} -#endif - #if HAVE_CRYPTO_THREADID // OpenSSL uniq id function. void ssl_id_function(CRYPTO_THREADID* thread_id) { -#if LL_WINDOWS // apr_os_thread_current() returns an unsigned long. +#if LL_WINDOWS // apr_os_thread_current() returns a pointer, CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); -#else // if it would return a pointer. +#else // else it returns an unsigned long. CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); #endif } @@ -193,6 +186,13 @@ ssl_dyn_create_function_type old_ssl_dyn_create_function; ssl_dyn_destroy_function_type old_ssl_dyn_destroy_function; ssl_dyn_lock_function_type old_ssl_dyn_lock_function; +#if LL_WINDOWS +static unsigned long __cdecl apr_os_thread_current_wrapper() +{ + return (unsigned long)apr_os_thread_current(); +} +#endif + // Set for openssl-1.0.1...1.0.1c. static bool need_renegotiation_hack = false; From ce2c6ab49e870db1bd9dbb9e45e0f70fc130fdb5 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 30 Jul 2012 02:45:39 +0200 Subject: [PATCH 052/123] Don't crash when libcurl wasn't compiled with support for libz --- indra/llmessage/aicurl.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index d079711d1..dc3c7e499 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -312,10 +312,19 @@ void initCurl(void (*flush_hook)()) { llwarns << "libcurl was not compiled with support for asynchronous name lookups!" << llendl; } + if (!version_info->ssl_version) + { + llerrs << "This libcurl has no SSL support!" << llendl; + } llinfos << "Successful initialization of libcurl " << version_info->version << " (0x" << std::hex << version_info->version_num << "), (" << - version_info->ssl_version << ", libz/" << version_info->libz_version << ")." << llendl; + version_info->ssl_version; + if (version_info->libz_version) + { + llcont << ", libz/" << version_info->libz_version; + } + llcont << ")." << llendl; // Detect SSL library used. gSSLlib = ssl_unknown; From 3de21156b56e341317e515ab37e51b1189ef00e7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 30 Jul 2012 02:46:48 +0200 Subject: [PATCH 053/123] Windows code improvements and debugging. Extended the DEBUG_WINDOWS_CODE_ON_LINUX hack to include ALL code. memset-zero sockaddr_in, and use WSASocket as per suggestion of Shyotl. --- indra/llmessage/aicurlthread.cpp | 101 +++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index deea1b404..3956e9f2f 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -143,8 +143,40 @@ static void FD_CLR(curl_socket_t s, windows_fd_set* fsp) #define fd_set windows_fd_set #define select windows_select +int WSAGetLastError(void) +{ + return errno; +} + +typedef char* LPTSTR; + +bool FormatMessage(int, void*, int e, int, LPTSTR error_str_ptr, int, void*) +{ + char* error_str = *(LPTSTR*)error_str_ptr; + error_str = strerror(e); + return true; +} + +void LocalFree(LPTSTR) +{ +} + +int const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0; +int const FORMAT_MESSAGE_FROM_SYSTEM = 0; +int const FORMAT_MESSAGE_IGNORE_INSERTS = 0; +int const INVALID_SOCKET = -1; +int const SOCKET_ERROR = -1; +int const WSAEWOULDBLOCK = EWOULDBLOCK; + +int closesocket(curl_socket_t fd) +{ + return close(fd); +} + #endif // DEBUG_WINDOWS_CODE_ON_LINUX +#define WINDOWS_CODE (LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) + #undef AICurlPrivate namespace AICurlPrivate { @@ -248,7 +280,7 @@ class PollSet // Return a pointer to the underlaying fd_set. fd_set* access(void) { return &mFdSet; } -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE // Return the largest fd set in mFdSet by refresh. curl_socket_t get_max_fd(void) const { return mMaxFdSet; } #endif @@ -275,7 +307,7 @@ class PollSet fd_set mFdSet; // Output variable for select(). (Re)initialized by calling refresh(). -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE curl_socket_t mMaxFd; // The largest filedescriptor in the array, or CURL_SOCKET_BAD when it is empty. curl_socket_t mMaxFdSet; // The largest filedescriptor set in mFdSet by refresh(), or CURL_SOCKET_BAD when it was empty. std::vector mCopiedFileDescriptors; // Filedescriptors copied by refresh to mFdSet. @@ -307,7 +339,7 @@ static size_t const MAXSIZE = llmax(1024, FD_SETSIZE); // Create an empty PollSet. PollSet::PollSet(void) : mFileDescriptors(new curl_socket_t [MAXSIZE]), mNrFds(0), mNext(0) -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE , mMaxFd(-1), mMaxFdSet(-1) #endif { @@ -319,7 +351,7 @@ void PollSet::add(curl_socket_t s) { llassert_always(mNrFds < (int)MAXSIZE); mFileDescriptors[mNrFds++] = s; -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE mMaxFd = llmax(mMaxFd, s); #endif } @@ -356,7 +388,7 @@ void PollSet::remove(curl_socket_t s) // index: 0 1 2 3 4 5 // a b c s d e curl_socket_t cur = mFileDescriptors[i]; // cur = 'e' -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE curl_socket_t max = -1; #endif while (cur != s) @@ -364,7 +396,7 @@ void PollSet::remove(curl_socket_t s) llassert(i > 0); curl_socket_t next = mFileDescriptors[--i]; // next = 'd' mFileDescriptors[i] = cur; // Overwrite 'd' with 'e'. -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE max = llmax(max, cur); // max is the maximum value in 'i' or higher. #endif cur = next; // cur = 'd' @@ -391,7 +423,7 @@ void PollSet::remove(curl_socket_t s) if (mNext > i) // i is where s was. --mNext; -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE // If s was the largest file descriptor, we have to update mMaxFd. if (s == mMaxFd) { @@ -408,7 +440,7 @@ void PollSet::remove(curl_socket_t s) // ALSO make sure that s is no longer set in mFdSet, or we might confuse libcurl by // calling curl_multi_socket_action for a socket that it told us to remove. -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE clr(s); #else // We have to use a custom implementation here, because we don't want to invalidate mIter. @@ -457,13 +489,13 @@ inline void PollSet::clr(curl_socket_t fd) refresh_t PollSet::refresh(void) { FD_ZERO(&mFdSet); -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE mCopiedFileDescriptors.clear(); #endif if (mNrFds == 0) { -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE mMaxFdSet = -1; #endif return empty_and_complete; @@ -478,7 +510,7 @@ refresh_t PollSet::refresh(void) if (mNrFds >= FD_SETSIZE) { llwarns << "PollSet::reset: More than FD_SETSIZE (" << FD_SETSIZE << ") file descriptors active!" << llendl; -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE // Calculate mMaxFdSet. // Run over FD_SETSIZE - 1 elements, starting at mNext, wrapping to 0 when we reach the end. int max = -1, i = mNext, count = 0; @@ -489,7 +521,7 @@ refresh_t PollSet::refresh(void) else { mNext = 0; // Start at the beginning if we copy everything anyway. -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE mMaxFdSet = mMaxFd; #endif } @@ -503,7 +535,7 @@ refresh_t PollSet::refresh(void) return not_complete_not_empty; } FD_SET(mFileDescriptors[i], &mFdSet); -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE mCopiedFileDescriptors.push_back(mFileDescriptors[i]); #endif if (++i == mNrFds) @@ -541,7 +573,7 @@ refresh_t PollSet::refresh(void) void PollSet::reset(void) { -#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX +#if WINDOWS_CODE mIter = 0; #else if (mCopiedFileDescriptors.empty()) @@ -557,7 +589,7 @@ void PollSet::reset(void) inline curl_socket_t PollSet::get(void) const { -#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX +#if WINDOWS_CODE return (mIter >= mFdSet.fd_count) ? CURL_SOCKET_BAD : mFdSet.fd_array[mIter]; #else return (mIter == mCopiedFileDescriptors.end()) ? CURL_SOCKET_BAD : *mIter; @@ -566,7 +598,7 @@ inline curl_socket_t PollSet::get(void) const void PollSet::next(void) { -#if LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX +#if WINDOWS_CODE llassert(mIter < mFdSet.fd_count); ++mIter; #else @@ -727,7 +759,7 @@ class AICurlThread : public LLThread void create_wakeup_fds(void); void cleanup_wakeup_fds(void); -#if (!LL_WINDOWS) +#if !WINDOWS_CODE //On Windows, single socket is used for communicating with itself! -SG curl_socket_t mWakeUpFd_in; #endif @@ -743,7 +775,7 @@ AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), -#if (!LL_WINDOWS) +#if !WINDOWS_CODE mWakeUpFd_in(CURL_SOCKET_BAD), #endif mWakeUpFd(CURL_SOCKET_BAD), @@ -760,7 +792,7 @@ AICurlThread::~AICurlThread() cleanup_wakeup_fds(); } -#if LL_WINDOWS +#if WINDOWS_CODE static std::string formatWSAError() { std::ostringstream r; @@ -771,7 +803,11 @@ static std::string formatWSAError() FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0, (LPTSTR)&error_str, 0, NULL)) { +#if LL_LINUX + r << " " << error_str; +#else r << " " << utf16str_to_utf8str(error_str); +#endif LocalFree(error_str); } else @@ -785,18 +821,26 @@ static std::string formatWSAError() // MAIN-THREAD void AICurlThread::create_wakeup_fds(void) { -#if LL_WINDOWS - mWakeUpFd = socket(AF_INET, SOCK_DGRAM, 0); //Maybe IPPROTO_UDP as last argument? -SG +#if WINDOWS_CODE +#if LL_LINUX + mWakeUpFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +#else + mWakeUpFd = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0); +#endif if(mWakeUpFd == INVALID_SOCKET) { llerrs << "Failed to create wake-up socket: " << formatWSAError() << llendl; } int error; sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_port = 0; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +#if LL_LINUX + socklen_t addrlen = sizeof(addr); +#else int addrlen = sizeof(addr); +#endif error = bind(mWakeUpFd, (sockaddr*) &addr, addrlen); if(error) { @@ -812,8 +856,13 @@ void AICurlThread::create_wakeup_fds(void) { llerrs << "Failed to connect wake-up socket: " <mWritePollSet->access(); // Calculate nfds (ignored on windows). -#if !(LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) +#if !WINDOWS_CODE curl_socket_t const max_rfd = llmax(multi_handle_w->mReadPollSet->get_max_fd(), mWakeUpFd); curl_socket_t const max_wfd = multi_handle_w->mWritePollSet->get_max_fd(); int nfds = llmax(max_rfd, max_wfd) + 1; From dd47123bde223c7c088bcfe968919c5a5cc1a9e1 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 31 Jul 2012 04:56:03 +0200 Subject: [PATCH 054/123] Add possibility to suppress function name prefix in debug output. Adds llinfos_nf et al. --- indra/llcommon/llerror.cpp | 18 +++++++++------ indra/llcommon/llerror.h | 46 +++++++++++++++++++++----------------- indra/llmessage/aicurl.cpp | 16 ++++++------- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index c537a76a8..7b362b230 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -963,10 +963,14 @@ namespace LLError settings_w->shouldLogCallCounter += 1; std::string class_name = className(site.mClassInfo); - std::string function_name = functionName(site.mFunction); - if (site.mClassInfo != typeid(NoClassInfo)) + std::string function_name; + if (site.mFunction) { - function_name = class_name + "::" + function_name; + function_name = functionName(site.mFunction); + if (site.mClassInfo != typeid(NoClassInfo)) + { + function_name = class_name + "::" + function_name; + } } ELevel compareLevel = settings_w->defaultLevel; @@ -976,7 +980,7 @@ namespace LLError // So, in increasing order of importance: // Default < Broad Tag < File < Class < Function < Narrow Tag ((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false) - || checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel) + || (site.mFunction && checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)) || checkLevelMap(settings_w->classLevelMap, class_name, compareLevel) || checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel) || ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false); @@ -1083,8 +1087,8 @@ namespace LLError default: prefix << "XXX"; break; }; - bool need_function = true; - if (site.mBroadTag && *site.mBroadTag != '\0') + bool need_function = site.mFunction; + if (need_function && site.mBroadTag && *site.mBroadTag != '\0') { prefix << "(\"" << site.mBroadTag << "\")"; #if LL_DEBUG @@ -1112,7 +1116,7 @@ namespace LLError #if LL_WINDOWS // DevStudio: __FUNCTION__ already includes the full class name #else - if (need_function && site.mClassInfo != typeid(NoClassInfo)) + if (site.mClassInfo != typeid(NoClassInfo)) { prefix << className(site.mClassInfo) << "::"; } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 34934c5c2..d0279f866 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -238,10 +238,10 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; See top of file for common usage. */ -#define lllog(level, broadTag, narrowTag, once) \ +#define lllog(level, broadTag, narrowTag, once, nofunction) \ do { \ static LLError::CallSite _site( \ - level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, broadTag, narrowTag, once);\ + level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), nofunction ? NULL : __FUNCTION__, broadTag, narrowTag, once);\ if (LL_UNLIKELY(_site.shouldLog())) \ { \ std::ostringstream* _out = LLError::Log::out(); \ @@ -255,33 +255,39 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; } while(0) // DEPRECATED: Use the new macros that allow tags and *look* like macros. -#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false) -#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false) -#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false) -#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false) +#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, false) +#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false, false) +#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false, false) +#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false, false) #define llcont (*_out) +// No function name +#define lldebugs_nf lllog(LLError::LEVEL_DEBUG, NULL, NULL, false, true) +#define llinfos_nf lllog(LLError::LEVEL_INFO, NULL, NULL, false, true) +#define llwarns_nf lllog(LLError::LEVEL_WARN, NULL, NULL, false, true) +#define llerrs_nf lllog(LLError::LEVEL_ERROR, NULL, NULL, false, true) + // NEW Macros for debugging, allow the passing of a string tag // One Tag -#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false) -#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false) -#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false) -#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false) +#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false, false) +#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false, false) +#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false, false) +#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false, false) // Two Tags -#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false) -#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false) -#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false) -#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false) +#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false, false) +#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false, false) +#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false, false) +#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false, false) // Only print the log message once (good for warnings or infos that would otherwise // spam the log file over and over, such as tighter loops). -#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true) -#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true) -#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true) -#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true) -#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true) -#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true) +#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true, false) +#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true, false) +#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true, false) +#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true, false) +#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true, false) +#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true, false) #define LL_ENDL llendl #define LL_CONT (*_out) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index dc3c7e499..989c9aee6 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -547,12 +547,12 @@ LLAtomicU32 Stats::multi_errors; //static void Stats::print(void) { - llinfos << "============ CURL STATS ============" << llendl; - llinfos << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl; - llinfos << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl; - llinfos << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; - llinfos << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl; - llinfos << "========= END OF CURL STATS =========" << llendl; + llinfos_nf << "============ CURL STATS ============" << llendl; + llinfos_nf << " Curl multi errors/calls : " << std::dec << multi_errors << "/" << multi_calls << llendl; + llinfos_nf << " Curl easy errors/calls : " << std::dec << easy_errors << "/" << easy_calls << llendl; + llinfos_nf << " curl_easy_init() errors/calls : " << std::dec << easy_init_errors << "/" << easy_init_calls << llendl; + llinfos_nf << " Current number of curl easy handles: " << std::dec << (easy_init_calls - easy_init_errors - easy_cleanup_calls) << llendl; + llinfos_nf << "========= END OF CURL STATS =========" << llendl; } // THREAD-SAFE @@ -806,7 +806,7 @@ void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* us setopt(CURLOPT_SSL_CTX_DATA, userdata ? this : NULL); } -#define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false) +#define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false, true) static size_t noHeaderCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { @@ -1127,7 +1127,7 @@ CurlResponderBuffer::CurlResponderBuffer() curl_easy_request_w->send_events_to(this); } -#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false) +#define llmaybeerrs lllog(LLApp::isRunning() ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false, true) // The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use). // The AIThreadSafeSimple is destructed first (right to left), so when we get here then the From c9715c5b0b7827d22acfea844cadff0c325d440a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 31 Jul 2012 04:57:13 +0200 Subject: [PATCH 055/123] Print Dout() and DoutEntering() debug output on windows. This is a bit of a hack currently and requires a recompile with -DDEBUG_CURLIO. --- indra/cwdebug/CMakeLists.txt | 2 + indra/cwdebug/debug.cc | 69 +++++++++++++++++++- indra/cwdebug/debug.h | 108 ++++++++++++++++++++++++++++++- indra/llmessage/aicurl.cpp | 24 ++++++- indra/llmessage/aicurlthread.cpp | 4 +- indra/newview/llstartup.cpp | 2 +- 6 files changed, 199 insertions(+), 10 deletions(-) diff --git a/indra/cwdebug/CMakeLists.txt b/indra/cwdebug/CMakeLists.txt index 9f423c736..cb67f94f7 100644 --- a/indra/cwdebug/CMakeLists.txt +++ b/indra/cwdebug/CMakeLists.txt @@ -34,6 +34,8 @@ if(NOT WORD_SIZE EQUAL 32) endif(WINDOWS) endif (NOT WORD_SIZE EQUAL 32) +add_definitions(-Dcwdebug_EXPORTS) + list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) add_library (cwdebug ${cwdebug_SOURCE_FILES}) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 5235ee8d6..2f25e5358 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -413,4 +413,71 @@ void cwdebug_backtrace(int n) } #endif -#endif // CWDEBUG +#elif defined(DEBUG_CURLIO) + +#include "debug.h" + +namespace debug +{ + +libcwd_do_type const libcw_do; +AI_THREADLOCAL int Indent::S_indentation; + +std::ostream& operator<<(std::ostream& os, Indent::print_nt) +{ + if (Indent::S_indentation) + os << std::string(Indent::S_indentation, ' '); + return os; +} + +std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s) +{ + static char const c2s_tab[7] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' }; + size_t size = b2s.mSize; + for (char const* p1 = b2s.mBuf; size > 0; --size, ++p1) + { + char c =*p1; + if ((c > 31 && c != 92 && c != 127) || (unsigned char)c > 159) + os.put(c); + else + { + os.put('\\'); + if (c > 6 && c < 14) + { + os.put(c2s_tab[c - 7]); + return os; + } + else if (c == 27) + { + os.put('e'); + return os; + } + else if (c == '\\') + { + os.put('\\'); + return os; + } + short old_fill = os.fill('0'); + std::ios_base::fmtflags old_flgs = os.flags(); + os.width(3); + os << std::oct << (int)((unsigned char)c); + os.setf(old_flgs); + os.fill(old_fill); + } + } + return os; +} + +namespace dc +{ + +fake_channel const warning(1, "WARNING "); +fake_channel const curl(1, "CURL "); +fake_channel const curlio(1, "CURLIO "); +fake_channel const statemachine(1, "STATEMACHINE"); +fake_channel const notice(1, "NOTICE "); + +} // namespace dc +} // namespace debug + +#endif diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index f148e7667..51fd95914 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -27,6 +27,111 @@ #ifndef CWDEBUG +#ifdef DEBUG_CURLIO + +#if LL_WINDOWS +#define CWD_DLLEXPORT __declspec(dllexport) +#define CWD_DLLIMPORT __declspec(dllimport) +#elif LL_LINUX +#define CWD_DLLEXPORT __attribute__ ((visibility("default"))) +#define CWD_DLLIMPORT +#else +#define CWD_DLLEXPORT +#define CWD_DLLIMPORT +#endif // LL_WINDOWS + +#if LL_COMMON_LINK_SHARED +#if defined(cwdebug_EXPORTS) +#define CWD_API CWD_DLLEXPORT +#else // cwdebug_EXPORTS +#define CWD_API CWD_DLLIMPORT +#endif // cwdebug_EXPORTS +#else // LL_COMMON_LINK_SHARED +#error LL_COMMON_LINK_SHARED not defined +#endif // LL_COMMON_LINK_SHARED + +// If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace +// some of the cwd macro's with something that generates viewer +// specific debug output. Note that this generates a LOT of +// output and should not normally be defined. + +#include + +#if LL_WINDOWS +#define AI_THREADLOCAL __declspec(thread) +#else +#define AI_THREADLOCAL __thread +#endif + +namespace debug { +namespace libcwd { + +struct buf2str { + buf2str(char const* buf, int size) : mBuf(buf), mSize(size) { } + char const* mBuf; + int mSize; +}; + +} // namespace libcwd + +extern CWD_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); + +inline void init() { } +struct libcwd_do_type { + void on() const { } +}; +extern CWD_API libcwd_do_type const libcw_do; +struct CWD_API Indent { + int M_indent; + static AI_THREADLOCAL int S_indentation; + enum print_nt { print }; + Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } + ~Indent() { S_indentation -= M_indent; } + friend CWD_API std::ostream& operator<<(std::ostream& os, print_nt); +}; + +namespace dc { + +struct fake_channel { + int mOn; + char const* mLabel; + fake_channel(int on, char const* label) : mOn(on), mLabel(label) { } + fake_channel(void) : mOn(0) { } + bool is_on() const { return mOn; } + bool is_off() const { return !mOn; } + void on() const { } + void off() const { } +}; +extern CWD_API fake_channel const warning; +extern CWD_API fake_channel const curl; +extern CWD_API fake_channel const curlio; +extern CWD_API fake_channel const statemachine; +extern CWD_API fake_channel const notice; + +} // namespace dc +} // namespace debug + +#define Debug(x) do { using namespace debug; x; } while(0) +#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define DoutEntering(a, b) \ + int __slviewer_debug_indentation = 2; \ + { \ + using namespace debug; \ + if ((a).mOn) \ + llinfos_nf << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ + else \ + __slviewer_debug_indentation = 0; \ + } \ + debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); + +#else // !DEBUG_CURLIO + +#define Debug(x) +#define Dout(a, b) +#define DoutEntering(a, b) + +#endif // !DEBUG_CURLIO + #ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info. #include @@ -36,9 +141,6 @@ #define AllocTag2(p, desc) #define AllocTag_dynamic_description(p, x) #define AllocTag(p, x) -#define Debug(x) -#define Dout(a, b) -#define DoutEntering(a, b) #define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b) #define ForAllDebugChannels(STATEMENT) #define ForAllDebugObjects(STATEMENT) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 989c9aee6..c68399059 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -886,9 +886,18 @@ void CurlEasyRequest::addHeader(char const* header) mHeaders = curl_slist_append(mHeaders, header); } -#ifdef CWDEBUG -static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr) +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) + +#ifndef CWDEBUG +#define LIBCWD_DEBUGCHANNELS 0 +#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; llinfos_nf << dc::curlio.mLabel << ": "; +#define LibcwDoutStream llcont +#define LibcwDoutScopeEnd llcont << llendl; } while(0) +#endif + +static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr) { +#ifdef CWDEBUG using namespace ::libcwd; CurlEasyRequest* request = (CurlEasyRequest*)user_ptr; @@ -898,6 +907,13 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t libcw_do.marker().assign(marker.str().data(), marker.str().size()); if (!debug::channels::dc::curlio.is_on()) debug::channels::dc::curlio.on(); +#else + if (infotype == CURLINFO_TEXT) + { + while (size > 0 && (buf[size - 1] == '\r' || buf[size - 1] == '\n')) + --size; + } +#endif LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) switch (infotype) { @@ -976,7 +992,9 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t else LibcwDoutStream << size << " bytes"; LibcwDoutScopeEnd; +#ifdef CWDEBUG libcw_do.pop_marker(); +#endif return 0; } #endif @@ -1041,7 +1059,7 @@ void CurlEasyRequest::applyDefaultOptions(void) if (dc::curlio.is_on()) { setopt(CURLOPT_VERBOSE, 1); - setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_callback); + setopt(CURLOPT_DEBUGFUNCTION, &curl_debug_cb); setopt(CURLOPT_DEBUGDATA, this); } ); diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 3956e9f2f..c5348125b 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -39,7 +39,7 @@ #endif #include -#define DEBUG_WINDOWS_CODE_ON_LINUX 0 +#define DEBUG_WINDOWS_CODE_ON_LINUX 1 #if DEBUG_WINDOWS_CODE_ON_LINUX @@ -1262,7 +1262,7 @@ MultiHandle::~MultiHandle() delete mReadPollSet; } -#ifdef CWDEBUG +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) #undef AI_CASE_RETURN #define AI_CASE_RETURN(x) do { case x: return #x; } while(0) char const* action_str(int action) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 10f10cc90..1400da474 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -253,7 +253,7 @@ extern S32 gStartImageHeight; // local globals // -#ifdef CWDEBUG +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) static bool gCurlIo; #endif From 783e86990ce6318ce65fe11e62c3a1c9fbd99e15 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 31 Jul 2012 19:42:15 +0200 Subject: [PATCH 056/123] Compile fixes --- indra/cwdebug/debug.h | 6 +++--- indra/llmessage/aicurlthread.cpp | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 51fd95914..0599d0598 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -81,12 +81,12 @@ struct libcwd_do_type { void on() const { } }; extern CWD_API libcwd_do_type const libcw_do; -struct CWD_API Indent { +struct Indent { int M_indent; static AI_THREADLOCAL int S_indentation; enum print_nt { print }; - Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } - ~Indent() { S_indentation -= M_indent; } + CWD_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } + CWD_API ~Indent() { S_indentation -= M_indent; } friend CWD_API std::ostream& operator<<(std::ostream& os, print_nt); }; diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index c5348125b..ea1c4b5ba 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -39,7 +39,11 @@ #endif #include -#define DEBUG_WINDOWS_CODE_ON_LINUX 1 +// On linux, add -DDEBUG_WINDOWS_CODE_ON_LINUX to test the windows code used in this file. +#if !defined(DEBUG_WINDOWS_CODE_ON_LINUX) || !defined(LL_LINUX) || defined(LL_RELEASE) +#undef DEBUG_WINDOWS_CODE_ON_LINUX +#define DEBUG_WINDOWS_CODE_ON_LINUX 0 +#endif #if DEBUG_WINDOWS_CODE_ON_LINUX From 0208942b840b631e4bd1bd8c9834d0be36f975a0 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 31 Jul 2012 21:28:37 +0200 Subject: [PATCH 057/123] Compile warning fix --- indra/cwdebug/debug.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 2f25e5358..34ce73738 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -457,7 +457,7 @@ std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s) os.put('\\'); return os; } - short old_fill = os.fill('0'); + std::ostream::char_type old_fill = os.fill('0'); std::ios_base::fmtflags old_flgs = os.flags(); os.width(3); os << std::oct << (int)((unsigned char)c); From 7f64668e551070f7f2596bd7943addf804db6034 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 31 Jul 2012 21:29:22 +0200 Subject: [PATCH 058/123] Compile warning fix --- indra/cwdebug/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 0599d0598..ce35a0e34 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -97,7 +97,7 @@ struct fake_channel { char const* mLabel; fake_channel(int on, char const* label) : mOn(on), mLabel(label) { } fake_channel(void) : mOn(0) { } - bool is_on() const { return mOn; } + bool is_on() const { return !!mOn; } bool is_off() const { return !mOn; } void on() const { } void off() const { } From 9c4f22d85b2ef93645e23995c8c43360d48b5f19 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 31 Jul 2012 21:30:05 +0200 Subject: [PATCH 059/123] Attempt at fixing linker errors... --- indra/cwdebug/debug.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index ce35a0e34..9501cd60c 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -74,8 +74,6 @@ struct buf2str { } // namespace libcwd -extern CWD_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); - inline void init() { } struct libcwd_do_type { void on() const { } @@ -84,12 +82,14 @@ extern CWD_API libcwd_do_type const libcw_do; struct Indent { int M_indent; static AI_THREADLOCAL int S_indentation; - enum print_nt { print }; + enum CWD_API print_nt { print }; CWD_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } CWD_API ~Indent() { S_indentation -= M_indent; } - friend CWD_API std::ostream& operator<<(std::ostream& os, print_nt); }; +extern CWD_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); +extern CWD_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); + namespace dc { struct fake_channel { From 32be05fa6ca53123a114542c6b189629af769b8c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 31 Jul 2012 22:37:49 +0200 Subject: [PATCH 060/123] Compile warning fixes. Type fix (SLL -> SSL) --- indra/llmessage/aicurl.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 6d3cbf4b0..2f90b8e6e 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -200,15 +200,15 @@ static bool need_renegotiation_hack = false; void ssl_init(void) { // The version identifier format is: MMNNFFPPS: major minor fix patch status. - int const compiled_openSLL_major = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; - int const compiled_openSLL_minor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; + int const compiled_openSSL_major = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; + int const compiled_openSSL_minor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; unsigned long const ssleay = SSLeay(); - int const linked_openSLL_major = (ssleay >> 28) & 0xff; - int const linked_openSLL_minor = (ssleay >> 20) & 0xff; + int const linked_openSSL_major = (ssleay >> 28) & 0xff; + int const linked_openSSL_minor = (ssleay >> 20) & 0xff; // Check if dynamically loaded version is compatible with the one we compiled against. // As off version 1.0.0 also minor versions are compatible. - if (linked_openSLL_major != compiled_openSLL_major || - (compiled_openSLL_major == 0 && linked_openSLL_minor != compiled_openSLL_minor)) + if (linked_openSSL_major != compiled_openSSL_major || + (linked_openSSL_major == 0 && linked_openSSL_minor != compiled_openSSL_minor)) { llerrs << "The viewer was compiled against " << OPENSSL_VERSION_TEXT << " but linked against " << SSLeay_version(SSLEAY_VERSION) << @@ -949,7 +949,7 @@ static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, { LibcwDoutStream << size << " bytes"; bool finished = false; - int i = 0; + size_t i = 0; while (i < size) { char c = buf[i]; From 048c57cf0c3c3265b6594384bd8e76d70a1e667a Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 31 Jul 2012 22:40:20 +0200 Subject: [PATCH 061/123] Linktime fix --- indra/cwdebug/debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 9501cd60c..e20ddda77 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -41,7 +41,7 @@ #endif // LL_WINDOWS #if LL_COMMON_LINK_SHARED -#if defined(cwdebug_EXPORTS) +#if defined(cwdebug_EXPORTS) || defined(llcommon_EXPORTS) #define CWD_API CWD_DLLEXPORT #else // cwdebug_EXPORTS #define CWD_API CWD_DLLIMPORT From 725cdc2d69d734f6b457ee426882f3be1af4b7c8 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 31 Jul 2012 23:04:45 +0200 Subject: [PATCH 062/123] Linux compile fixes --- indra/cwdebug/debug.cc | 2 +- indra/cwdebug/debug.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 34ce73738..18b51aad0 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -421,7 +421,7 @@ namespace debug { libcwd_do_type const libcw_do; -AI_THREADLOCAL int Indent::S_indentation; +CWD_TLS int Indent::S_indentation; std::ostream& operator<<(std::ostream& os, Indent::print_nt) { diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index e20ddda77..d49a61512 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -58,9 +58,11 @@ #include #if LL_WINDOWS -#define AI_THREADLOCAL __declspec(thread) +#define CWD_API_TLS __declspec(thread) +#define CWD_TLS __declspec(thread) #else -#define AI_THREADLOCAL __thread +#define CWD_API_TLS CWD_API __thread +#define CWD_TLS __thread #endif namespace debug { @@ -81,7 +83,7 @@ struct libcwd_do_type { extern CWD_API libcwd_do_type const libcw_do; struct Indent { int M_indent; - static AI_THREADLOCAL int S_indentation; + static CWD_API_TLS int S_indentation; enum CWD_API print_nt { print }; CWD_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } CWD_API ~Indent() { S_indentation -= M_indent; } From 4650636e5aefa3275991f9e574dc82b0d9304b64 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Thu, 2 Aug 2012 22:33:01 +0200 Subject: [PATCH 063/123] Use TCP socket pair instead of UDP --- indra/llmessage/aicurlthread.cpp | 240 ++++++++++++++++++++----------- 1 file changed, 158 insertions(+), 82 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index ea1c4b5ba..59457c54d 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -763,10 +763,7 @@ class AICurlThread : public LLThread void create_wakeup_fds(void); void cleanup_wakeup_fds(void); -#if !WINDOWS_CODE - //On Windows, single socket is used for communicating with itself! -SG curl_socket_t mWakeUpFd_in; -#endif curl_socket_t mWakeUpFd; int mZeroTimeOut; @@ -779,9 +776,7 @@ AICurlThread* AICurlThread::sInstance = NULL; // MAIN-THREAD AICurlThread::AICurlThread(void) : LLThread("AICurlThread"), -#if !WINDOWS_CODE mWakeUpFd_in(CURL_SOCKET_BAD), -#endif mWakeUpFd(CURL_SOCKET_BAD), mZeroTimeOut(0), mRunning(true), mWakeUpFlag(false) { @@ -796,7 +791,7 @@ AICurlThread::~AICurlThread() cleanup_wakeup_fds(); } -#if WINDOWS_CODE +#if LL_WINDOWS static std::string formatWSAError() { std::ostringstream r; @@ -807,11 +802,7 @@ static std::string formatWSAError() FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0, (LPTSTR)&error_str, 0, NULL)) { -#if LL_LINUX - r << " " << error_str; -#else r << " " << utf16str_to_utf8str(error_str); -#endif LocalFree(error_str); } else @@ -820,58 +811,112 @@ static std::string formatWSAError() } return r.str(); } +#else if WINDOWS_CODE +static std::string formatWSAError() +{ + return "NOT IMPLEMENTED"; +} +#endif + +#if LL_WINDOWS + /* Copyright 2007, 2010 by Nathan C. Myers + * This code is Free Software. It may be copied freely, in original or + * modified form, subject only to the restrictions that (1) the author is + * relieved from all responsibilities for any use for any purpose, and (2) + * this copyright notice must be retained, unchanged, in its entirety. If + * for any reason the author might be held responsible for any consequences + * of copying or use, license is withheld. + */ +static int dumb_socketpair(SOCKET socks[2], bool make_overlapped) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + SOCKET listener; + int e; + socklen_t addrlen = sizeof(a.inaddr); + DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0); + int reuse = 1; + + if (socks == 0) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) + return SOCKET_ERROR; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + socks[0] = socks[1] = INVALID_SOCKET; + do { + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) + break; + if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + memset(&a, 0, sizeof(a)); + if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) + break; + // win32 getsockname may only set the port number, p=0.0005. + // ( http://msdn.microsoft.com/library/ms738543.aspx ): + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_family = AF_INET; + + if (listen(listener, 1) == SOCKET_ERROR) + break; + + socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); + if (socks[0] == INVALID_SOCKET) + break; + if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + socks[1] = accept(listener, NULL, NULL); + if (socks[1] == INVALID_SOCKET) + break; + + closesocket(listener); + return 0; + + } while (0); + + e = WSAGetLastError(); + closesocket(listener); + closesocket(socks[0]); + closesocket(socks[1]); + WSASetLastError(e); + return SOCKET_ERROR; +} +#else +int dumb_socketpair(int socks[2], int dummy) +{ + (void) dummy; + return socketpair(AF_LOCAL, SOCK_STREAM, 0, socks); +} #endif // MAIN-THREAD void AICurlThread::create_wakeup_fds(void) { #if WINDOWS_CODE -#if LL_LINUX - mWakeUpFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); -#else - mWakeUpFd = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0); -#endif - if(mWakeUpFd == INVALID_SOCKET) - { - llerrs << "Failed to create wake-up socket: " << formatWSAError() << llendl; - } - int error; - sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); -#if LL_LINUX - socklen_t addrlen = sizeof(addr); -#else - int addrlen = sizeof(addr); -#endif - error = bind(mWakeUpFd, (sockaddr*) &addr, addrlen); - if(error) - { - llerrs << "Failed to bind wake-up socket: " << formatWSAError() << llendl; - } - error = getsockname(mWakeUpFd, (sockaddr*) &addr, &addrlen); - if(error) - { - llerrs << "Failed to detect wake-up socket: " << formatWSAError() << llendl; - } - error = connect(mWakeUpFd, (sockaddr*) &addr, addrlen); - if(error) - { - llerrs << "Failed to connect wake-up socket: " < 0) + { + // Data was read from the pipe. + got_data = true; + if (len < sizeof(buf)) + break; + } + else if (len == SOCKET_ERROR) + { + // An error occurred. + if (errno == EWOULDBLOCK) { - got_data = true; - llinfos << "Received wakeup signal" << llendl; + if (got_data) + break; + // There was no data, even though select() said so. If this ever happens at all(?), lets just return and enter select() again. + return; } - } while(len != SOCKET_ERROR); - int error = WSAGetLastError(); - llinfos << "left loop, errorlevel " << error << llendl; - if(error != WSAEWOULDBLOCK) - { - llerrs << "Wake-up socket drain error: " << formatWSAError() << llendl; - } - if(!got_data) - { - llinfos << "Wakeup called but socket is empty" << llendl; + else if (errno == EINTR) + { + continue; + } + else + { + llerrs << "read(3) from mWakeUpFd: " << formatWSAError() << llendl; + return; + } + } + else + { + // pipe(2) returned 0. + llwarns << "read(3) from mWakeUpFd returned 0, indicating that the pipe on the other end was closed! Shutting down curl thread." << llendl; + closesocket(mWakeUpFd); + mWakeUpFd = CURL_SOCKET_BAD; + mRunning = false; return; + } } - llinfos << "Passing control to process_commands" << llendl; #else // If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR]. // If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read. From 08d72b12abf60486647add8578a33fd808b3b695 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 2 Aug 2012 23:03:03 +0200 Subject: [PATCH 064/123] Fix compilation on linux. Remove trailing ^M's --- indra/llmessage/aicurlthread.cpp | 160 +++++++++++++++---------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 59457c54d..98116398f 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -811,94 +811,94 @@ static std::string formatWSAError() } return r.str(); } -#else if WINDOWS_CODE +#elif WINDOWS_CODE static std::string formatWSAError() { - return "NOT IMPLEMENTED"; + return strerror(errno); } #endif #if LL_WINDOWS - /* Copyright 2007, 2010 by Nathan C. Myers - * This code is Free Software. It may be copied freely, in original or - * modified form, subject only to the restrictions that (1) the author is - * relieved from all responsibilities for any use for any purpose, and (2) - * this copyright notice must be retained, unchanged, in its entirety. If - * for any reason the author might be held responsible for any consequences - * of copying or use, license is withheld. + /* Copyright 2007, 2010 by Nathan C. Myers + * This code is Free Software. It may be copied freely, in original or + * modified form, subject only to the restrictions that (1) the author is + * relieved from all responsibilities for any use for any purpose, and (2) + * this copyright notice must be retained, unchanged, in its entirety. If + * for any reason the author might be held responsible for any consequences + * of copying or use, license is withheld. */ -static int dumb_socketpair(SOCKET socks[2], bool make_overlapped) -{ - union { - struct sockaddr_in inaddr; - struct sockaddr addr; - } a; - SOCKET listener; - int e; - socklen_t addrlen = sizeof(a.inaddr); - DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0); - int reuse = 1; - - if (socks == 0) { - WSASetLastError(WSAEINVAL); - return SOCKET_ERROR; - } - - listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listener == INVALID_SOCKET) - return SOCKET_ERROR; - - memset(&a, 0, sizeof(a)); - a.inaddr.sin_family = AF_INET; - a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - a.inaddr.sin_port = 0; - - socks[0] = socks[1] = INVALID_SOCKET; - do { - if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, - (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) - break; - if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) - break; - - memset(&a, 0, sizeof(a)); - if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) - break; - // win32 getsockname may only set the port number, p=0.0005. - // ( http://msdn.microsoft.com/library/ms738543.aspx ): - a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - a.inaddr.sin_family = AF_INET; - - if (listen(listener, 1) == SOCKET_ERROR) - break; - - socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); - if (socks[0] == INVALID_SOCKET) - break; - if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) - break; - - socks[1] = accept(listener, NULL, NULL); - if (socks[1] == INVALID_SOCKET) - break; - - closesocket(listener); - return 0; - - } while (0); - - e = WSAGetLastError(); - closesocket(listener); - closesocket(socks[0]); - closesocket(socks[1]); - WSASetLastError(e); - return SOCKET_ERROR; +static int dumb_socketpair(SOCKET socks[2], bool make_overlapped) +{ + union { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + SOCKET listener; + int e; + socklen_t addrlen = sizeof(a.inaddr); + DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0); + int reuse = 1; + + if (socks == 0) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) + return SOCKET_ERROR; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + socks[0] = socks[1] = INVALID_SOCKET; + do { + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) + break; + if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + memset(&a, 0, sizeof(a)); + if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) + break; + // win32 getsockname may only set the port number, p=0.0005. + // ( http://msdn.microsoft.com/library/ms738543.aspx ): + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_family = AF_INET; + + if (listen(listener, 1) == SOCKET_ERROR) + break; + + socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags); + if (socks[0] == INVALID_SOCKET) + break; + if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + socks[1] = accept(listener, NULL, NULL); + if (socks[1] == INVALID_SOCKET) + break; + + closesocket(listener); + return 0; + + } while (0); + + e = WSAGetLastError(); + closesocket(listener); + closesocket(socks[0]); + closesocket(socks[1]); + WSASetLastError(e); + return SOCKET_ERROR; } #else -int dumb_socketpair(int socks[2], int dummy) -{ - (void) dummy; - return socketpair(AF_LOCAL, SOCK_STREAM, 0, socks); +int dumb_socketpair(int socks[2], int dummy) +{ + (void) dummy; + return socketpair(AF_LOCAL, SOCK_STREAM, 0, socks); } #endif @@ -907,7 +907,7 @@ void AICurlThread::create_wakeup_fds(void) { #if WINDOWS_CODE //SGTODO - SOCKET socks[2]; + curl_socket_t socks[2]; if (dumb_socketpair(socks, false) == SOCKET_ERROR) { llerrs << "Failed to generate wake-up socket pair" << formatWSAError() << llendl; From af4ceb06580d6c16a8d90056b0e35f39b98c761c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 3 Aug 2012 00:53:54 +0200 Subject: [PATCH 065/123] Crash on exit fix. --- indra/newview/statemachine/aistatemachine.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index 71a8ac47f..675317cb1 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -610,11 +610,19 @@ void AIStateMachine::flush(void) { AIStateMachine& statemachine(iter->statemachine()); if (statemachine.abortable()) - statemachine.abort(); + { + // We can't safely call abort() here for non-running (run() was called, but they we're initialized yet) statemachines, + // because that might call kill() which in some cases is undesirable (ie, when it is owned by a partent that will + // also call abort() on it when it is aborted itself). + if (statemachine.running()) + statemachine.abort(); + else + statemachine.idle(); // Stop the statemachine from starting, in the next loop with batch == 0. + } } for (int batch = 0;; ++batch) { - // Run mainloop until all state machines are idle. + // Run mainloop until all state machines are idle (batch == 0) or deleted (batch == 1). for(;;) { { From 98c740f2047790766f6db5b4cb09e50249a8deff Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 3 Aug 2012 00:56:41 +0200 Subject: [PATCH 066/123] Remove debug output --- indra/llmessage/aicurlthread.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 98116398f..86eef7764 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -981,9 +981,7 @@ void AICurlThread::wakeup_thread(void) #if WINDOWS_CODE //SGTODO - Dout(dc::curl, "ENTERING send()"); int len = send(mWakeUpFd_in, "!", 1, 0); - Dout(dc::curl, "LEAVING send()"); if (len == SOCKET_ERROR) { llerrs << "Send to wake-up socket failed: " << formatWSAError() << llendl; From 761439cc8d2ce88cdcde68fed0140ea9320777c6 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sun, 5 Aug 2012 08:20:31 +0200 Subject: [PATCH 067/123] Setting socket pair non-blocking --- indra/llmessage/aicurlthread.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 98116398f..5d9b04e5e 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -911,12 +911,22 @@ void AICurlThread::create_wakeup_fds(void) if (dumb_socketpair(socks, false) == SOCKET_ERROR) { llerrs << "Failed to generate wake-up socket pair" << formatWSAError() << llendl; + return; } - else - { - mWakeUpFd = socks[0]; - mWakeUpFd_in = socks[1]; - } + u_long nonblocking_enable = TRUE; + int error = ioctlsocket(socks[0], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up socket nonblocking: " << formatWSAError() << llendl; + } + llassert(nonblocking_enable); + error = ioctlsocket(socks[1], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up input socket nonblocking: " << formatWSAError() << llendl; + } + mWakeUpFd = socks[0]; + mWakeUpFd_in = socks[1]; #else int pipefd[2]; if (pipe(pipefd)) From caef97ad360697b6584861e00b3e8836ce96826f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 5 Aug 2012 18:51:21 +0200 Subject: [PATCH 068/123] Print all libcurl calls if -DDEBUG_CURLIO. Don't create static lib for cwdebug. Basically, cmake doesn't support linking static libs into a shared lib. The correct way is to just specify source files in subdirectories directly as source files of the shared library. This patch changes that. Also, after this commit, when DEBUG_CURLIO is defined, every call to libcurl is printed to llinfos (or to dc::curl when using libcwd). --- indra/CMakeLists.txt | 1 - indra/cmake/Cwdebug.cmake | 21 +- indra/cwdebug/CMakeLists.txt | 41 -- indra/cwdebug/debug.h | 2 +- indra/cwdebug/debug_libcurl.cc | 769 +++++++++++++++++++++++++++ indra/cwdebug/debug_libcurl.h | 83 +++ indra/llcommon/CMakeLists.txt | 7 +- indra/llmessage/aicurl.h | 3 +- indra/llmessage/llhttpclient.cpp | 1 - indra/llmessage/llproxy.cpp | 1 - indra/newview/hipporestrequest.cpp | 3 +- indra/newview/llappviewer.cpp | 1 - indra/newview/llpanellogin.cpp | 1 - indra/newview/lluserauth.cpp | 1 - indra/newview/llviewerassetstorage.h | 1 - indra/newview/llwlparammanager.cpp | 1 - 16 files changed, 878 insertions(+), 59 deletions(-) delete mode 100644 indra/cwdebug/CMakeLists.txt create mode 100644 indra/cwdebug/debug_libcurl.cc create mode 100644 indra/cwdebug/debug_libcurl.h diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index e81965cc6..3ec482baf 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -46,7 +46,6 @@ endif(NOT STANDALONE) add_custom_target(prepare DEPENDS ${prepare_depends}) add_subdirectory(cmake) -add_subdirectory(${LIBS_OPEN_PREFIX}cwdebug) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) diff --git a/indra/cmake/Cwdebug.cmake b/indra/cmake/Cwdebug.cmake index 9568a625f..f68c7a897 100644 --- a/indra/cmake/Cwdebug.cmake +++ b/indra/cmake/Cwdebug.cmake @@ -1 +1,20 @@ -set(CWDEBUG_LIBRARIES cwdebug) +include_directories (${CMAKE_SOURCE_DIR}/cwdebug) + +set(cwdebug_SOURCE_FILES + ${CMAKE_SOURCE_DIR}/cwdebug/debug.cc + ${CMAKE_SOURCE_DIR}/cwdebug/debug_libcurl.cc + ) + +set(cwdebug_HEADER_FILES + ${CMAKE_SOURCE_DIR}/cwdebug/cwdebug.h + ${CMAKE_SOURCE_DIR}/cwdebug/sys.h + ${CMAKE_SOURCE_DIR}/cwdebug/debug.h + ${CMAKE_SOURCE_DIR}/cwdebug/debug_ostream_operators.h + ${CMAKE_SOURCE_DIR}/cwdebug/debug_libcurl.h + ) + +set_source_files_properties(${cwdebug_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) + diff --git a/indra/cwdebug/CMakeLists.txt b/indra/cwdebug/CMakeLists.txt deleted file mode 100644 index cb67f94f7..000000000 --- a/indra/cwdebug/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# -*- cmake -*- - -project(cwdebug) - -include(00-Common) -include(LLCommon) -include(LLMath) -include(LLMessage) -include(LLVFS) - -include_directories (${CMAKE_CURRENT_SOURCE_DIR}) - -set(cwdebug_SOURCE_FILES - debug.cc - ) - -set(cwdebug_HEADER_FILES - CMakeLists.txt - - cwdebug.h - sys.h - debug.h - debug_ostream_operators.h - ) - -set_source_files_properties(${cwdebug_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) - -if(NOT WORD_SIZE EQUAL 32) - if(WINDOWS) - add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif (NOT WORD_SIZE EQUAL 32) - -add_definitions(-Dcwdebug_EXPORTS) - -list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) - -add_library (cwdebug ${cwdebug_SOURCE_FILES}) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index d49a61512..4161e18f7 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -41,7 +41,7 @@ #endif // LL_WINDOWS #if LL_COMMON_LINK_SHARED -#if defined(cwdebug_EXPORTS) || defined(llcommon_EXPORTS) +#if defined(llcommon_EXPORTS) #define CWD_API CWD_DLLEXPORT #else // cwdebug_EXPORTS #define CWD_API CWD_DLLIMPORT diff --git a/indra/cwdebug/debug_libcurl.cc b/indra/cwdebug/debug_libcurl.cc new file mode 100644 index 000000000..10aa0f28e --- /dev/null +++ b/indra/cwdebug/debug_libcurl.cc @@ -0,0 +1,769 @@ +#ifdef DEBUG_CURLIO + +#include "sys.h" +#include +#include +#include +#define COMPILING_DEBUG_LIBCURL_CC +#include "debug_libcurl.h" +#include "../llcommon/llerror.h" + +static struct curl_slist unchanged_slist; + +std::ostream& operator<<(std::ostream& os, struct curl_slist const& slist) +{ + struct curl_slist const* ptr = &slist; + if (ptr == &unchanged_slist) + os << " "; + else + { + os << "(curl_slist)@0x" << std::hex << (size_t)ptr << std::dec << "{"; + do + { + os << '"' << ptr->data << '"'; + ptr = ptr->next; + if (ptr) + os << ", "; + } + while(ptr); + os << "}"; + } + return os; +} + +#define CASEPRINT(x) case x: os << #x; break + +std::ostream& operator<<(std::ostream& os, CURLINFO info) +{ + switch (info) + { + CASEPRINT(CURLINFO_EFFECTIVE_URL); + CASEPRINT(CURLINFO_RESPONSE_CODE); + CASEPRINT(CURLINFO_TOTAL_TIME); + CASEPRINT(CURLINFO_NAMELOOKUP_TIME); + CASEPRINT(CURLINFO_CONNECT_TIME); + CASEPRINT(CURLINFO_PRETRANSFER_TIME); + CASEPRINT(CURLINFO_SIZE_UPLOAD); + CASEPRINT(CURLINFO_SIZE_DOWNLOAD); + CASEPRINT(CURLINFO_SPEED_DOWNLOAD); + CASEPRINT(CURLINFO_SPEED_UPLOAD); + CASEPRINT(CURLINFO_HEADER_SIZE); + CASEPRINT(CURLINFO_REQUEST_SIZE); + CASEPRINT(CURLINFO_SSL_VERIFYRESULT); + CASEPRINT(CURLINFO_FILETIME); + CASEPRINT(CURLINFO_CONTENT_LENGTH_DOWNLOAD); + CASEPRINT(CURLINFO_CONTENT_LENGTH_UPLOAD); + CASEPRINT(CURLINFO_STARTTRANSFER_TIME); + CASEPRINT(CURLINFO_CONTENT_TYPE); + CASEPRINT(CURLINFO_REDIRECT_TIME); + CASEPRINT(CURLINFO_REDIRECT_COUNT); + CASEPRINT(CURLINFO_PRIVATE); + CASEPRINT(CURLINFO_HTTP_CONNECTCODE); + CASEPRINT(CURLINFO_HTTPAUTH_AVAIL); + CASEPRINT(CURLINFO_PROXYAUTH_AVAIL); + CASEPRINT(CURLINFO_OS_ERRNO); + CASEPRINT(CURLINFO_NUM_CONNECTS); + CASEPRINT(CURLINFO_SSL_ENGINES); + CASEPRINT(CURLINFO_COOKIELIST); + CASEPRINT(CURLINFO_LASTSOCKET); + CASEPRINT(CURLINFO_FTP_ENTRY_PATH); + CASEPRINT(CURLINFO_REDIRECT_URL); + CASEPRINT(CURLINFO_PRIMARY_IP); + CASEPRINT(CURLINFO_APPCONNECT_TIME); + CASEPRINT(CURLINFO_CERTINFO); + CASEPRINT(CURLINFO_CONDITION_UNMET); + CASEPRINT(CURLINFO_RTSP_SESSION_ID); + CASEPRINT(CURLINFO_RTSP_CLIENT_CSEQ); + CASEPRINT(CURLINFO_RTSP_SERVER_CSEQ); + CASEPRINT(CURLINFO_RTSP_CSEQ_RECV); + CASEPRINT(CURLINFO_PRIMARY_PORT); + CASEPRINT(CURLINFO_LOCAL_IP); + CASEPRINT(CURLINFO_LOCAL_PORT); + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLcode code) +{ + switch(code) + { + CASEPRINT(CURLE_OK); + CASEPRINT(CURLE_UNSUPPORTED_PROTOCOL); + CASEPRINT(CURLE_FAILED_INIT); + CASEPRINT(CURLE_URL_MALFORMAT); + CASEPRINT(CURLE_NOT_BUILT_IN); + CASEPRINT(CURLE_COULDNT_RESOLVE_PROXY); + CASEPRINT(CURLE_COULDNT_RESOLVE_HOST); + CASEPRINT(CURLE_COULDNT_CONNECT); + CASEPRINT(CURLE_FTP_WEIRD_SERVER_REPLY); + CASEPRINT(CURLE_REMOTE_ACCESS_DENIED); + CASEPRINT(CURLE_FTP_ACCEPT_FAILED); + CASEPRINT(CURLE_FTP_WEIRD_PASS_REPLY); + CASEPRINT(CURLE_FTP_ACCEPT_TIMEOUT); + CASEPRINT(CURLE_FTP_WEIRD_PASV_REPLY); + CASEPRINT(CURLE_FTP_WEIRD_227_FORMAT); + CASEPRINT(CURLE_FTP_CANT_GET_HOST); + CASEPRINT(CURLE_OBSOLETE16); + CASEPRINT(CURLE_FTP_COULDNT_SET_TYPE); + CASEPRINT(CURLE_PARTIAL_FILE); + CASEPRINT(CURLE_FTP_COULDNT_RETR_FILE); + CASEPRINT(CURLE_OBSOLETE20); + CASEPRINT(CURLE_QUOTE_ERROR); + CASEPRINT(CURLE_HTTP_RETURNED_ERROR); + CASEPRINT(CURLE_WRITE_ERROR); + CASEPRINT(CURLE_OBSOLETE24); + CASEPRINT(CURLE_UPLOAD_FAILED); + CASEPRINT(CURLE_READ_ERROR); + CASEPRINT(CURLE_OUT_OF_MEMORY); + CASEPRINT(CURLE_OPERATION_TIMEDOUT); + CASEPRINT(CURLE_OBSOLETE29); + CASEPRINT(CURLE_FTP_PORT_FAILED); + CASEPRINT(CURLE_FTP_COULDNT_USE_REST); + CASEPRINT(CURLE_OBSOLETE32); + CASEPRINT(CURLE_RANGE_ERROR); + CASEPRINT(CURLE_HTTP_POST_ERROR); + CASEPRINT(CURLE_SSL_CONNECT_ERROR); + CASEPRINT(CURLE_BAD_DOWNLOAD_RESUME); + CASEPRINT(CURLE_FILE_COULDNT_READ_FILE); + CASEPRINT(CURLE_LDAP_CANNOT_BIND); + CASEPRINT(CURLE_LDAP_SEARCH_FAILED); + CASEPRINT(CURLE_OBSOLETE40); + CASEPRINT(CURLE_FUNCTION_NOT_FOUND); + CASEPRINT(CURLE_ABORTED_BY_CALLBACK); + CASEPRINT(CURLE_BAD_FUNCTION_ARGUMENT); + CASEPRINT(CURLE_OBSOLETE44); + CASEPRINT(CURLE_INTERFACE_FAILED); + CASEPRINT(CURLE_OBSOLETE46); + CASEPRINT(CURLE_TOO_MANY_REDIRECTS ); + CASEPRINT(CURLE_UNKNOWN_OPTION); + CASEPRINT(CURLE_TELNET_OPTION_SYNTAX ); + CASEPRINT(CURLE_OBSOLETE50); + CASEPRINT(CURLE_PEER_FAILED_VERIFICATION); + CASEPRINT(CURLE_GOT_NOTHING); + CASEPRINT(CURLE_SSL_ENGINE_NOTFOUND); + CASEPRINT(CURLE_SSL_ENGINE_SETFAILED); + CASEPRINT(CURLE_SEND_ERROR); + CASEPRINT(CURLE_RECV_ERROR); + CASEPRINT(CURLE_OBSOLETE57); + CASEPRINT(CURLE_SSL_CERTPROBLEM); + CASEPRINT(CURLE_SSL_CIPHER); + CASEPRINT(CURLE_SSL_CACERT); + CASEPRINT(CURLE_BAD_CONTENT_ENCODING); + CASEPRINT(CURLE_LDAP_INVALID_URL); + CASEPRINT(CURLE_FILESIZE_EXCEEDED); + CASEPRINT(CURLE_USE_SSL_FAILED); + CASEPRINT(CURLE_SEND_FAIL_REWIND); + CASEPRINT(CURLE_SSL_ENGINE_INITFAILED); + CASEPRINT(CURLE_LOGIN_DENIED); + CASEPRINT(CURLE_TFTP_NOTFOUND); + CASEPRINT(CURLE_TFTP_PERM); + CASEPRINT(CURLE_REMOTE_DISK_FULL); + CASEPRINT(CURLE_TFTP_ILLEGAL); + CASEPRINT(CURLE_TFTP_UNKNOWNID); + CASEPRINT(CURLE_REMOTE_FILE_EXISTS); + CASEPRINT(CURLE_TFTP_NOSUCHUSER); + CASEPRINT(CURLE_CONV_FAILED); + CASEPRINT(CURLE_CONV_REQD); + CASEPRINT(CURLE_SSL_CACERT_BADFILE); + CASEPRINT(CURLE_REMOTE_FILE_NOT_FOUND); + CASEPRINT(CURLE_SSH); + CASEPRINT(CURLE_SSL_SHUTDOWN_FAILED); + CASEPRINT(CURLE_AGAIN); + CASEPRINT(CURLE_SSL_CRL_BADFILE); + CASEPRINT(CURLE_SSL_ISSUER_ERROR); + CASEPRINT(CURLE_FTP_PRET_FAILED); + CASEPRINT(CURLE_RTSP_CSEQ_ERROR); + CASEPRINT(CURLE_RTSP_SESSION_ERROR); + CASEPRINT(CURLE_FTP_BAD_FILE_LIST); + CASEPRINT(CURLE_CHUNK_FAILED); + default: + os << (code == CURL_LAST ? ""; + } + return os; +} + +struct AICURL; +struct AICURLM; + +std::ostream& operator<<(std::ostream& os, AICURL* curl) +{ + os << "(CURL*)0x" << std::hex << (size_t)curl << std::dec; + return os; +} + +std::ostream& operator<<(std::ostream& os, AICURLM* curl) +{ + os << "(CURLM*)0x" << std::hex << (size_t)curl << std::dec; + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLoption option) +{ + switch(option) + { + CASEPRINT(CURLOPT_WRITEDATA); + CASEPRINT(CURLOPT_URL); + CASEPRINT(CURLOPT_PORT); + CASEPRINT(CURLOPT_PROXY); + CASEPRINT(CURLOPT_USERPWD); + CASEPRINT(CURLOPT_PROXYUSERPWD); + CASEPRINT(CURLOPT_RANGE); + CASEPRINT(CURLOPT_READDATA); + CASEPRINT(CURLOPT_ERRORBUFFER); + CASEPRINT(CURLOPT_WRITEFUNCTION); + CASEPRINT(CURLOPT_READFUNCTION); + CASEPRINT(CURLOPT_TIMEOUT); + CASEPRINT(CURLOPT_INFILESIZE); + CASEPRINT(CURLOPT_POSTFIELDS); + CASEPRINT(CURLOPT_REFERER); + CASEPRINT(CURLOPT_FTPPORT); + CASEPRINT(CURLOPT_USERAGENT); + CASEPRINT(CURLOPT_LOW_SPEED_LIMIT); + CASEPRINT(CURLOPT_LOW_SPEED_TIME); + CASEPRINT(CURLOPT_RESUME_FROM); + CASEPRINT(CURLOPT_COOKIE); + CASEPRINT(CURLOPT_RTSPHEADER); + CASEPRINT(CURLOPT_HTTPPOST); + CASEPRINT(CURLOPT_SSLCERT); + CASEPRINT(CURLOPT_KEYPASSWD); + CASEPRINT(CURLOPT_CRLF); + CASEPRINT(CURLOPT_QUOTE); + CASEPRINT(CURLOPT_HEADERDATA); + CASEPRINT(CURLOPT_COOKIEFILE); + CASEPRINT(CURLOPT_SSLVERSION); + CASEPRINT(CURLOPT_TIMECONDITION); + CASEPRINT(CURLOPT_TIMEVALUE); + CASEPRINT(CURLOPT_CUSTOMREQUEST); + CASEPRINT(CURLOPT_STDERR); + CASEPRINT(CURLOPT_POSTQUOTE); + CASEPRINT(CURLOPT_WRITEINFO); + CASEPRINT(CURLOPT_VERBOSE); + CASEPRINT(CURLOPT_HEADER); + CASEPRINT(CURLOPT_NOPROGRESS); + CASEPRINT(CURLOPT_NOBODY); + CASEPRINT(CURLOPT_FAILONERROR); + CASEPRINT(CURLOPT_UPLOAD); + CASEPRINT(CURLOPT_POST); + CASEPRINT(CURLOPT_DIRLISTONLY); + CASEPRINT(CURLOPT_APPEND); + CASEPRINT(CURLOPT_NETRC); + CASEPRINT(CURLOPT_FOLLOWLOCATION); + CASEPRINT(CURLOPT_TRANSFERTEXT); + CASEPRINT(CURLOPT_PUT); + CASEPRINT(CURLOPT_PROGRESSFUNCTION); + CASEPRINT(CURLOPT_PROGRESSDATA); + CASEPRINT(CURLOPT_AUTOREFERER); + CASEPRINT(CURLOPT_PROXYPORT); + CASEPRINT(CURLOPT_HTTPPROXYTUNNEL); + CASEPRINT(CURLOPT_INTERFACE); + CASEPRINT(CURLOPT_KRBLEVEL); + CASEPRINT(CURLOPT_SSL_VERIFYPEER); + CASEPRINT(CURLOPT_CAINFO); + CASEPRINT(CURLOPT_MAXREDIRS); + CASEPRINT(CURLOPT_FILETIME); + CASEPRINT(CURLOPT_TELNETOPTIONS); + CASEPRINT(CURLOPT_MAXCONNECTS); + CASEPRINT(CURLOPT_CLOSEPOLICY); + CASEPRINT(CURLOPT_FRESH_CONNECT); + CASEPRINT(CURLOPT_FORBID_REUSE); + CASEPRINT(CURLOPT_RANDOM_FILE); + CASEPRINT(CURLOPT_EGDSOCKET); + CASEPRINT(CURLOPT_CONNECTTIMEOUT); + CASEPRINT(CURLOPT_HEADERFUNCTION); + CASEPRINT(CURLOPT_HTTPGET); + CASEPRINT(CURLOPT_SSL_VERIFYHOST); + CASEPRINT(CURLOPT_COOKIEJAR); + CASEPRINT(CURLOPT_SSL_CIPHER_LIST); + CASEPRINT(CURLOPT_HTTP_VERSION); + CASEPRINT(CURLOPT_FTP_USE_EPSV); + CASEPRINT(CURLOPT_SSLCERTTYPE); + CASEPRINT(CURLOPT_SSLKEY); + CASEPRINT(CURLOPT_SSLKEYTYPE); + CASEPRINT(CURLOPT_SSLENGINE); + CASEPRINT(CURLOPT_SSLENGINE_DEFAULT); + CASEPRINT(CURLOPT_DNS_USE_GLOBAL_CACHE); + CASEPRINT(CURLOPT_DNS_CACHE_TIMEOUT); + CASEPRINT(CURLOPT_PREQUOTE); + CASEPRINT(CURLOPT_DEBUGFUNCTION); + CASEPRINT(CURLOPT_DEBUGDATA); + CASEPRINT(CURLOPT_COOKIESESSION); + CASEPRINT(CURLOPT_CAPATH); + CASEPRINT(CURLOPT_BUFFERSIZE); + CASEPRINT(CURLOPT_NOSIGNAL); + CASEPRINT(CURLOPT_SHARE); + CASEPRINT(CURLOPT_PROXYTYPE); + CASEPRINT(CURLOPT_ACCEPT_ENCODING); + CASEPRINT(CURLOPT_PRIVATE); + CASEPRINT(CURLOPT_HTTP200ALIASES); + CASEPRINT(CURLOPT_UNRESTRICTED_AUTH); + CASEPRINT(CURLOPT_FTP_USE_EPRT); + CASEPRINT(CURLOPT_HTTPAUTH); + CASEPRINT(CURLOPT_SSL_CTX_FUNCTION); + CASEPRINT(CURLOPT_SSL_CTX_DATA); + CASEPRINT(CURLOPT_FTP_CREATE_MISSING_DIRS); + CASEPRINT(CURLOPT_PROXYAUTH); + CASEPRINT(CURLOPT_FTP_RESPONSE_TIMEOUT); + CASEPRINT(CURLOPT_IPRESOLVE); + CASEPRINT(CURLOPT_MAXFILESIZE); + CASEPRINT(CURLOPT_INFILESIZE_LARGE); + CASEPRINT(CURLOPT_RESUME_FROM_LARGE); + CASEPRINT(CURLOPT_MAXFILESIZE_LARGE); + CASEPRINT(CURLOPT_NETRC_FILE); + CASEPRINT(CURLOPT_USE_SSL); + CASEPRINT(CURLOPT_POSTFIELDSIZE_LARGE); + CASEPRINT(CURLOPT_TCP_NODELAY); + CASEPRINT(CURLOPT_FTPSSLAUTH); + CASEPRINT(CURLOPT_IOCTLFUNCTION); + CASEPRINT(CURLOPT_IOCTLDATA); + CASEPRINT(CURLOPT_FTP_ACCOUNT); + CASEPRINT(CURLOPT_COOKIELIST); + CASEPRINT(CURLOPT_IGNORE_CONTENT_LENGTH); + CASEPRINT(CURLOPT_FTP_SKIP_PASV_IP); + CASEPRINT(CURLOPT_FTP_FILEMETHOD); + CASEPRINT(CURLOPT_LOCALPORT); + CASEPRINT(CURLOPT_LOCALPORTRANGE); + CASEPRINT(CURLOPT_CONNECT_ONLY); + CASEPRINT(CURLOPT_CONV_FROM_NETWORK_FUNCTION); + CASEPRINT(CURLOPT_CONV_TO_NETWORK_FUNCTION); + CASEPRINT(CURLOPT_CONV_FROM_UTF8_FUNCTION); + CASEPRINT(CURLOPT_MAX_SEND_SPEED_LARGE); + CASEPRINT(CURLOPT_MAX_RECV_SPEED_LARGE); + CASEPRINT(CURLOPT_FTP_ALTERNATIVE_TO_USER); + CASEPRINT(CURLOPT_SOCKOPTFUNCTION); + CASEPRINT(CURLOPT_SOCKOPTDATA); + CASEPRINT(CURLOPT_SSL_SESSIONID_CACHE); + CASEPRINT(CURLOPT_SSH_AUTH_TYPES); + CASEPRINT(CURLOPT_SSH_PUBLIC_KEYFILE); + CASEPRINT(CURLOPT_SSH_PRIVATE_KEYFILE); + CASEPRINT(CURLOPT_FTP_SSL_CCC); + CASEPRINT(CURLOPT_TIMEOUT_MS); + CASEPRINT(CURLOPT_CONNECTTIMEOUT_MS); + CASEPRINT(CURLOPT_HTTP_TRANSFER_DECODING); + CASEPRINT(CURLOPT_HTTP_CONTENT_DECODING); + CASEPRINT(CURLOPT_NEW_FILE_PERMS); + CASEPRINT(CURLOPT_NEW_DIRECTORY_PERMS); + CASEPRINT(CURLOPT_POSTREDIR); + CASEPRINT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); + CASEPRINT(CURLOPT_OPENSOCKETFUNCTION); + CASEPRINT(CURLOPT_OPENSOCKETDATA); + CASEPRINT(CURLOPT_COPYPOSTFIELDS); + CASEPRINT(CURLOPT_PROXY_TRANSFER_MODE); + CASEPRINT(CURLOPT_SEEKFUNCTION); + CASEPRINT(CURLOPT_SEEKDATA); + CASEPRINT(CURLOPT_CRLFILE); + CASEPRINT(CURLOPT_ISSUERCERT); + CASEPRINT(CURLOPT_ADDRESS_SCOPE); + CASEPRINT(CURLOPT_CERTINFO); + CASEPRINT(CURLOPT_USERNAME); + CASEPRINT(CURLOPT_PASSWORD); + CASEPRINT(CURLOPT_PROXYUSERNAME); + CASEPRINT(CURLOPT_PROXYPASSWORD); + CASEPRINT(CURLOPT_NOPROXY); + CASEPRINT(CURLOPT_TFTP_BLKSIZE); + CASEPRINT(CURLOPT_SOCKS5_GSSAPI_SERVICE); + CASEPRINT(CURLOPT_SOCKS5_GSSAPI_NEC); + CASEPRINT(CURLOPT_PROTOCOLS); + CASEPRINT(CURLOPT_REDIR_PROTOCOLS); + CASEPRINT(CURLOPT_SSH_KNOWNHOSTS); + CASEPRINT(CURLOPT_SSH_KEYFUNCTION); + CASEPRINT(CURLOPT_SSH_KEYDATA); + CASEPRINT(CURLOPT_MAIL_FROM); + CASEPRINT(CURLOPT_MAIL_RCPT); + CASEPRINT(CURLOPT_FTP_USE_PRET); + CASEPRINT(CURLOPT_RTSP_REQUEST); + CASEPRINT(CURLOPT_RTSP_SESSION_ID); + CASEPRINT(CURLOPT_RTSP_STREAM_URI); + CASEPRINT(CURLOPT_RTSP_TRANSPORT); + CASEPRINT(CURLOPT_RTSP_CLIENT_CSEQ); + CASEPRINT(CURLOPT_RTSP_SERVER_CSEQ); + CASEPRINT(CURLOPT_INTERLEAVEDATA); + CASEPRINT(CURLOPT_INTERLEAVEFUNCTION); + CASEPRINT(CURLOPT_WILDCARDMATCH); + CASEPRINT(CURLOPT_CHUNK_BGN_FUNCTION); + CASEPRINT(CURLOPT_CHUNK_END_FUNCTION); + CASEPRINT(CURLOPT_FNMATCH_FUNCTION); + CASEPRINT(CURLOPT_CHUNK_DATA); + CASEPRINT(CURLOPT_FNMATCH_DATA); + CASEPRINT(CURLOPT_RESOLVE); + CASEPRINT(CURLOPT_TLSAUTH_USERNAME); + CASEPRINT(CURLOPT_TLSAUTH_PASSWORD); + CASEPRINT(CURLOPT_TLSAUTH_TYPE); + CASEPRINT(CURLOPT_TRANSFER_ENCODING); + CASEPRINT(CURLOPT_CLOSESOCKETFUNCTION); + CASEPRINT(CURLOPT_CLOSESOCKETDATA); + CASEPRINT(CURLOPT_GSSAPI_DELEGATION); + CASEPRINT(CURLOPT_DNS_SERVERS); + CASEPRINT(CURLOPT_ACCEPTTIMEOUT_MS); + CASEPRINT(CURLOPT_TCP_KEEPALIVE); + CASEPRINT(CURLOPT_TCP_KEEPIDLE); + CASEPRINT(CURLOPT_TCP_KEEPINTVL); + CASEPRINT(CURLOPT_SSL_OPTIONS); + CASEPRINT(CURLOPT_MAIL_AUTH); + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLMoption option) +{ + switch(option) + { + CASEPRINT(CURLMOPT_SOCKETFUNCTION); + CASEPRINT(CURLMOPT_SOCKETDATA); + CASEPRINT(CURLMOPT_PIPELINING); + CASEPRINT(CURLMOPT_TIMERFUNCTION); + CASEPRINT(CURLMOPT_TIMERDATA); + CASEPRINT(CURLMOPT_MAXCONNECTS); + default: + os << ""; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CURLMsg* msg) +{ + if (msg) + { + os << "(CURLMsg*){"; + if (msg->msg == CURLMSG_DONE) + os << "CURLMSG_DONE"; + else + os << msg->msg; + os << ", " << (AICURL*)msg->easy_handle << ", 0x" << std::hex << (size_t)msg->data.whatever << std::dec << '}'; + } + else + os << "(CURLMsg*)NULL"; + return os; +} + +struct EvBitmask { + int mBitmask; + EvBitmask(int mask) : mBitmask(mask) { } +}; + +std::ostream& operator<<(std::ostream& os, EvBitmask const& bitmask) +{ + int m = bitmask.mBitmask; + if (m == 0) + os << '0'; + if ((m & CURL_CSELECT_IN)) + { + os << "CURL_CSELECT_IN"; + if ((m & (CURL_CSELECT_OUT|CURL_CSELECT_ERR))) + os << '|'; + } + if ((m & CURL_CSELECT_OUT)) + { + os << "CURL_CSELECT_OUT"; + if ((m & CURL_CSELECT_ERR)) + os << '|'; + } + if ((m & CURL_CSELECT_ERR)) + { + os << "CURL_CSELECT_ERR"; + } + return os; +} + +extern "C" { + +void debug_curl_easy_cleanup(CURL* handle) +{ + curl_easy_cleanup(handle); + Dout(dc::curl, "curl_easy_cleanup(" << (AICURL*)handle << ")"); +} + +CURL* debug_curl_easy_duphandle(CURL* handle) +{ + CURL* ret; + ret = curl_easy_duphandle(handle); + Dout(dc::curl, "curl_easy_duphandle(" << (AICURL*)handle << ") = " << (AICURL*)ret); + return ret; +} + +char* debug_curl_easy_escape(CURL* curl, char* url, int length) +{ + char* ret; + ret = curl_easy_escape(curl, url, length); + Dout(dc::curl, "curl_easy_escape(" << curl << ", \"" << url << "\", " << length << ") = \"" << ret << '"'); + return ret; +} + +CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...) +{ + CURLcode ret; + va_list ap; + union param_type { + long* long_ptr; + char* char_ptr; + curl_slist* curl_slist_ptr; + double* double_ptr; + } param; + va_start(ap, info); + param.long_ptr = va_arg(ap, long*); + va_end(ap); + ret = curl_easy_getinfo(curl, info, param.long_ptr); + if (info == CURLINFO_PRIVATE) + { + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.char_ptr << std::dec << ") = " << ret); + } + else + { + switch((info & CURLINFO_TYPEMASK)) + { + case CURLINFO_STRING: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char*){ \"" << (ret == CURLE_OK ? param.char_ptr : " ") << "\" }) = " << ret); + break; + case CURLINFO_LONG: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (long*){ " << (ret == CURLE_OK ? *param.long_ptr : 0L) << "L }) = " << ret); + break; + case CURLINFO_DOUBLE: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (double*){" << (ret == CURLE_OK ? *param.double_ptr : 0.) << "}) = " << ret); + break; + case CURLINFO_SLIST: + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist*){ " << (ret == CURLE_OK ? *param.curl_slist_ptr : unchanged_slist) << " }) = " << ret); + break; + } + } + return ret; +} + +CURL* debug_curl_easy_init(void) +{ + CURL* ret; + ret = curl_easy_init(); + Dout(dc::curl, "curl_easy_init() = " << (AICURL*)ret); + return ret; +} + +CURLcode debug_curl_easy_pause(CURL* handle, int bitmask) +{ + CURLcode ret; + ret = curl_easy_pause(handle, bitmask); + Dout(dc::curl, "curl_easy_pause(" << (AICURL*)handle << ", 0x" << std::hex << bitmask << std::dec << ") = " << ret); + return ret; +} + +CURLcode debug_curl_easy_perform(CURL* handle) +{ + CURLcode ret; + ret = curl_easy_perform(handle); + Dout(dc::curl, "curl_easy_perform(" << (AICURL*)handle << ") = " << ret); + return ret; +} + +void debug_curl_easy_reset(CURL* handle) +{ + curl_easy_reset(handle); + Dout(dc::curl, "curl_easy_reset(" << (AICURL*)handle << ")"); +} + +CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) +{ + CURLcode ret; + va_list ap; + union param_type { + long along; + void* ptr; + curl_off_t offset; + } param; + va_start(ap, option); + param.ptr = va_arg(ap, void*); + va_end(ap); + ret = curl_easy_setopt(handle, option, param.ptr); + switch ((option / 10000) * 10000) + { + case CURLOPTTYPE_LONG: + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret); + break; + case CURLOPTTYPE_OBJECTPOINT: + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_FUNCTIONPOINT: + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_OFF_T: + Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); + break; + } + return ret; +} + +char const* debug_curl_easy_strerror(CURLcode errornum) +{ + char const* ret; + ret = curl_easy_strerror(errornum); + Dout(dc::curl, "curl_easy_strerror(" << errornum << ") = \"" << ret << '"'); + return ret; +} + +char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength) +{ + char* ret; + ret = curl_easy_unescape(curl, url, inlength, outlength); + Dout(dc::curl, "curl_easy_unescape(" << curl << ", \"" << url << "\", " << inlength << ", " << ((ret && outlength) ? *outlength : 1) << ") = \"" << ret << '"'); + return ret; +} + +void debug_curl_free(char* ptr) +{ + curl_free(ptr); + Dout(dc::curl, "curl_free(0x" << std::hex << (size_t)ptr << std::dec << ")"); +} + +time_t debug_curl_getdate(char const* datestring, time_t* now) +{ + time_t ret; + ret = curl_getdate(datestring, now); + Dout(dc::curl, "curl_getdate(\"" << datestring << "\", " << (now == NULL ? "NULL" : "") << ") = " << ret); + return ret; +} + +void debug_curl_global_cleanup(void) +{ + curl_global_cleanup(); + Dout(dc::curl, "curl_global_cleanup()"); +} + +CURLcode debug_curl_global_init(long flags) +{ + CURLcode ret; + ret = curl_global_init(flags); + Dout(dc::curl, "curl_global_init(0x" << std::hex << flags << std::dec << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle) +{ + CURLMcode ret; + ret = curl_multi_add_handle(multi_handle, easy_handle); + Dout(dc::curl, "curl_multi_add_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr) +{ + CURLMcode ret; + ret = curl_multi_assign(multi_handle, sockfd, sockptr); + Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << sockfd << ", " << sockptr << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle) +{ + CURLMcode ret; + ret = curl_multi_cleanup(multi_handle); + Dout(dc::curl, "curl_multi_cleanup(" << (AICURLM*)multi_handle << ") = " << ret); + return ret; +} + +CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue) +{ + CURLMsg* ret; + ret = curl_multi_info_read(multi_handle, msgs_in_queue); + Dout(dc::curl, "curl_multi_info_read(" << (AICURLM*)multi_handle << ", {" << *msgs_in_queue << "}) = " << ret); + return ret; +} + +CURLM* debug_curl_multi_init(void) +{ + CURLM* ret; + ret = curl_multi_init(); + Dout(dc::curl, "curl_multi_init() = " << (AICURLM*)ret); + return ret; +} + +CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle) +{ + CURLMcode ret; + ret = curl_multi_remove_handle(multi_handle, easy_handle); + Dout(dc::curl, "curl_multi_remove_handle(" << (AICURLM*)multi_handle << ", " << (AICURL*)easy_handle << ") = " << ret); + return ret; +} + +CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) +{ + CURLMcode ret; + va_list ap; + union param_type { + long along; + void* ptr; + curl_off_t offset; + } param; + va_start(ap, option); + param.ptr = va_arg(ap, void*); + va_end(ap); + ret = curl_multi_setopt(multi_handle, option, param.ptr); + switch ((option / 10000) * 10000) + { + case CURLOPTTYPE_LONG: + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret); + break; + case CURLOPTTYPE_OBJECTPOINT: + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_FUNCTIONPOINT: + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + break; + case CURLOPTTYPE_OFF_T: + Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); + break; + } + return ret; +} + +CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles) +{ + CURLMcode ret; + ret = curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles); + Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << sockfd << + ", " << EvBitmask(ev_bitmask) << ", {" << (ret == CURLM_OK ? *running_handles : 0) << "}) = " << ret); + return ret; +} + +char const* debug_curl_multi_strerror(CURLMcode errornum) +{ + char const* ret; + ret = curl_multi_strerror(errornum); + Dout(dc::curl, "curl_multi_strerror(" << errornum << ") = \"" << ret << '"'); + return ret; +} + +struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string) +{ + struct curl_slist* ret; + ret = curl_slist_append(list, string); + Dout(dc::curl, "curl_slist_append((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ", \"" << string << "\") = " << *ret); + return ret; +} + +void debug_curl_slist_free_all(struct curl_slist* list) +{ + curl_slist_free_all(list); + Dout(dc::curl, "curl_slist_free_all((curl_slist)@0x" << std::hex << (size_t)list << std::dec << ")"); +} + +char* debug_curl_unescape(char const* url, int length) +{ + char* ret; + ret = curl_unescape(url, length); + Dout(dc::curl, "curl_unescape(\"" << url << "\", " << length << ") = \"" << ret << '"'); + return ret; +} + +char* debug_curl_version(void) +{ + char* ret; + ret = curl_version(); + Dout(dc::curl, "curl_version() = \"" << ret << '"'); + return ret; +} + +} + +#else +int debug_libcurl_dummy; // I thought some OS didn't like empty source files. +#endif diff --git a/indra/cwdebug/debug_libcurl.h b/indra/cwdebug/debug_libcurl.h new file mode 100644 index 000000000..bcf15e0f0 --- /dev/null +++ b/indra/cwdebug/debug_libcurl.h @@ -0,0 +1,83 @@ +#ifndef DEBUG_LIBCURL +#define DEBUG_LIBCURL + +#ifdef DEBUG_CURLIO + +#include +#include "debug.h" + +extern "C" { + +extern CWD_API void debug_curl_easy_cleanup(CURL* handle); +extern CWD_API CURL* debug_curl_easy_duphandle(CURL* handle); +extern CWD_API char* debug_curl_easy_escape(CURL* curl, char* url, int length); +extern CWD_API CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...); +extern CWD_API CURL* debug_curl_easy_init(void); +extern CWD_API CURLcode debug_curl_easy_pause(CURL* handle, int bitmask); +extern CWD_API CURLcode debug_curl_easy_perform(CURL* handle); +extern CWD_API void debug_curl_easy_reset(CURL* handle); +extern CWD_API CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...); +extern CWD_API char const* debug_curl_easy_strerror(CURLcode errornum); +extern CWD_API char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength); +extern CWD_API void debug_curl_free(char* ptr); +extern CWD_API time_t debug_curl_getdate(char const* datestring, time_t* now); +extern CWD_API void debug_curl_global_cleanup(void); +extern CWD_API CURLcode debug_curl_global_init(long flags); +extern CWD_API CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle); +extern CWD_API CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr); +extern CWD_API CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle); +extern CWD_API CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue); +extern CWD_API CURLM* debug_curl_multi_init(void); +extern CWD_API CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle); +extern CWD_API CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...); +extern CWD_API CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles); +extern CWD_API char const* debug_curl_multi_strerror(CURLMcode errornum); +extern CWD_API struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string); +extern CWD_API void debug_curl_slist_free_all(struct curl_slist* list); +extern CWD_API char* debug_curl_unescape(char const* url, int length); +extern CWD_API char* debug_curl_version(void); + +} + +#ifndef COMPILING_DEBUG_LIBCURL_CC + +#ifdef curl_easy_setopt +#undef curl_easy_setopt +#undef curl_easy_getinfo +#undef curl_multi_setopt +#endif + +#define curl_easy_cleanup(handle) debug_curl_easy_cleanup(handle) +#define curl_easy_duphandle(handle) debug_curl_easy_duphandle(handle) +#define curl_easy_escape(curl, url, length) debug_curl_easy_escape(curl, url, length) +#define curl_easy_getinfo(curl, info, param) debug_curl_easy_getinfo(curl, info, param) +#define curl_easy_init() debug_curl_easy_init() +#define curl_easy_pause(handle, bitmask) debug_curl_easy_pause(handle, bitmask) +#define curl_easy_perform(handle) debug_curl_easy_perform(handle) +#define curl_easy_reset(handle) debug_curl_easy_reset(handle) +#define curl_easy_setopt(handle, option, param) debug_curl_easy_setopt(handle, option, param) +#define curl_easy_strerror(errornum) debug_curl_easy_strerror(errornum) +#define curl_easy_unescape(curl, url, inlength, outlength) debug_curl_easy_unescape(curl, url, inlength, outlength) +#define curl_free(ptr) debug_curl_free(ptr) +#define curl_getdate(datestring, now) debug_curl_getdate(datestring, now) +#define curl_global_cleanup() debug_curl_global_cleanup() +#define curl_global_init(flags) debug_curl_global_init(flags) +#define curl_multi_add_handle(multi_handle, easy_handle) debug_curl_multi_add_handle(multi_handle, easy_handle) +#define curl_multi_assign(multi_handle, sockfd, sockptr) debug_curl_multi_assign(multi_handle, sockfd, sockptr) +#define curl_multi_cleanup(multi_handle) debug_curl_multi_cleanup(multi_handle) +#define curl_multi_info_read(multi_handle, msgs_in_queue) debug_curl_multi_info_read(multi_handle, msgs_in_queue) +#define curl_multi_init() debug_curl_multi_init() +#define curl_multi_remove_handle(multi_handle, easy_handle) debug_curl_multi_remove_handle(multi_handle, easy_handle) +#define curl_multi_setopt(multi_handle, option, param) debug_curl_multi_setopt(multi_handle, option, param) +#define curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) debug_curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles) +#define curl_multi_strerror(errornum) debug_curl_multi_strerror(errornum) +#define curl_slist_append(list, string) debug_curl_slist_append(list, string) +#define curl_slist_free_all(list) debug_curl_slist_free_all(list) +#define curl_unescape(url, length) debug_curl_unescape(url, length) +#define curl_version() debug_curl_version() + +#endif // !COMPILING_DEBUG_LIBCURL_CC + +#endif // DEBUG_CURLIO + +#endif // DEBUG_LIBCURL diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 29feeebc4..4be690734 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -250,6 +250,7 @@ set(llcommon_HEADER_FILES set_source_files_properties(${llcommon_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) +list(APPEND llcommon_SOURCE_FILES ${cwdebug_SOURCE_FILES}) list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) add_library (llcommon SHARED ${llcommon_SOURCE_FILES}) @@ -262,15 +263,9 @@ target_link_libraries( ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} ${BOOST_REGEX_LIBRARY} - ${CWDEBUG_LIBRARIES} ${CORESERVICES_LIBRARY} ) -if (LINUX) - # When linking with llcommon later, we do not want to link with cwdebug.a again. - set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz") -endif (LINUX) - if (DARWIN) # Don't embed a full path in the library's install name set_target_properties( diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index 90803e050..4752d1b45 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -37,7 +37,8 @@ #include #include #include -#include // CURL, CURLM, CURLMcode, CURLoption, curl_*_callback +#include // Needed for files that include this header (also for aicurlprivate.h). +#include "debug_libcurl.h" // Make sure we don't use this option: it is not thread-safe. #undef CURLOPT_DNS_USE_GLOBAL_CACHE diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 355eebb77..7320ccb0e 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -38,7 +38,6 @@ #include "lluri.h" #include "message.h" -#include const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index d46c70f04..448b6606b 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -29,7 +29,6 @@ #include "llproxy.h" #include -#include #include "llapr.h" #include "llcurl.h" diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index 17875f27e..4cd0cc34c 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -6,7 +6,6 @@ #ifndef CURL_STATICLIB #define CURL_STATICLIB 1 #endif -#include #include #include @@ -15,6 +14,8 @@ #include #include +#include +#include "debug_libcurl.h" // ******************************************************************** diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 96b6166e4..e2c395737 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -63,7 +63,6 @@ #include "llviewerjoystick.h" #include "llfloaterjoystick.h" #include "llares.h" -#include "llcurl.h" #include "llfloatersnapshot.h" #include "lltexturestats.h" #include "llviewerwindow.h" diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 045b722a4..3483f2577 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -49,7 +49,6 @@ #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ #include "llcombobox.h" -#include "llcurl.h" #include "llviewercontrol.h" #include "llfloaterabout.h" #include "llfloatertest.h" diff --git a/indra/newview/lluserauth.cpp b/indra/newview/lluserauth.cpp index f2f928850..29930a96e 100644 --- a/indra/newview/lluserauth.cpp +++ b/indra/newview/lluserauth.cpp @@ -47,7 +47,6 @@ #include "stringize.h" // NOTE: MUST include these after otherincludes since queue gets redefined!?!! -#include #include diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 15d9a748e..7b85f55d7 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -34,7 +34,6 @@ #define LLVIEWERASSETSTORAGE_H #include "llassetstorage.h" -//#include "curl/curl.h" class LLVFile; diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index a87487f55..284da40cd 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -77,7 +77,6 @@ #include "llviewerregion.h" #include "llassetuploadresponders.h" -#include "curl/curl.h" #include "llstreamtools.h" // [RLVa:KB] - Checked: 2011-09-04 (RLVa-1.4.1a) | Added: RLVa-1.4.1a From 65a1aae629949dcb0b91ab6298fc34c60f09d8ac Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 5 Aug 2012 18:55:27 +0200 Subject: [PATCH 069/123] Bug fix curl_easy_setopt expects a long int. Before this patch, uninitialized memory was read, leading to extreme long time out, instead of the intended disabled time out (so, in practise this patch has little effect). This bug was discovered with the previous commit. --- indra/newview/llxmlrpctransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index b160e2c33..9d729b8da 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -250,7 +250,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) /* Setting the DNS cache timeout to -1 disables it completely. This might help with bug #503 */ - curlEasyRequest_w->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1); + curlEasyRequest_w->setopt(CURLOPT_DNS_CACHE_TIMEOUT, -1L); curlEasyRequest_w->addHeader("Content-Type: text/xml"); From e14be5c8c24d4a63ac3d8b4823e336b96c1905e1 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 7 Aug 2012 01:33:29 +0200 Subject: [PATCH 070/123] Make debug_libcurl.cc compile with libcurl 7.21 and higher --- indra/cwdebug/debug_libcurl.cc | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/indra/cwdebug/debug_libcurl.cc b/indra/cwdebug/debug_libcurl.cc index 10aa0f28e..0858ded92 100644 --- a/indra/cwdebug/debug_libcurl.cc +++ b/indra/cwdebug/debug_libcurl.cc @@ -8,6 +8,13 @@ #include "debug_libcurl.h" #include "../llcommon/llerror.h" +#define CURL_VERSION(major, minor, patch) \ + (LIBCURL_VERSION_MAJOR > major || \ + (LIBCURL_VERSION_MAJOR == major && \ + LIBCURL_VERSION_MINOR > minor || \ + (LIBCURL_VERSION_MINOR == minor && \ + LIBCURL_VERSION_PATCH >= patch))) + static struct curl_slist unchanged_slist; std::ostream& operator<<(std::ostream& os, struct curl_slist const& slist) @@ -93,15 +100,21 @@ std::ostream& operator<<(std::ostream& os, CURLcode code) CASEPRINT(CURLE_UNSUPPORTED_PROTOCOL); CASEPRINT(CURLE_FAILED_INIT); CASEPRINT(CURLE_URL_MALFORMAT); +#if CURL_VERSION(7, 21, 5) CASEPRINT(CURLE_NOT_BUILT_IN); +#endif CASEPRINT(CURLE_COULDNT_RESOLVE_PROXY); CASEPRINT(CURLE_COULDNT_RESOLVE_HOST); CASEPRINT(CURLE_COULDNT_CONNECT); CASEPRINT(CURLE_FTP_WEIRD_SERVER_REPLY); CASEPRINT(CURLE_REMOTE_ACCESS_DENIED); +#if 0 CASEPRINT(CURLE_FTP_ACCEPT_FAILED); +#endif CASEPRINT(CURLE_FTP_WEIRD_PASS_REPLY); +#if 0 CASEPRINT(CURLE_FTP_ACCEPT_TIMEOUT); +#endif CASEPRINT(CURLE_FTP_WEIRD_PASV_REPLY); CASEPRINT(CURLE_FTP_WEIRD_227_FORMAT); CASEPRINT(CURLE_FTP_CANT_GET_HOST); @@ -137,7 +150,11 @@ std::ostream& operator<<(std::ostream& os, CURLcode code) CASEPRINT(CURLE_INTERFACE_FAILED); CASEPRINT(CURLE_OBSOLETE46); CASEPRINT(CURLE_TOO_MANY_REDIRECTS ); +#if CURL_VERSION(7, 21, 5) CASEPRINT(CURLE_UNKNOWN_OPTION); +#else + CASEPRINT(CURLE_UNKNOWN_TELNET_OPTION); +#endif CASEPRINT(CURLE_TELNET_OPTION_SYNTAX ); CASEPRINT(CURLE_OBSOLETE50); CASEPRINT(CURLE_PEER_FAILED_VERIFICATION); @@ -294,7 +311,11 @@ std::ostream& operator<<(std::ostream& os, CURLoption option) CASEPRINT(CURLOPT_NOSIGNAL); CASEPRINT(CURLOPT_SHARE); CASEPRINT(CURLOPT_PROXYTYPE); +#if CURL_VERSION(7, 21, 6) CASEPRINT(CURLOPT_ACCEPT_ENCODING); +#else + CASEPRINT(CURLOPT_ENCODING); +#endif CASEPRINT(CURLOPT_PRIVATE); CASEPRINT(CURLOPT_HTTP200ALIASES); CASEPRINT(CURLOPT_UNRESTRICTED_AUTH); @@ -386,21 +407,37 @@ std::ostream& operator<<(std::ostream& os, CURLoption option) CASEPRINT(CURLOPT_FNMATCH_FUNCTION); CASEPRINT(CURLOPT_CHUNK_DATA); CASEPRINT(CURLOPT_FNMATCH_DATA); +#if CURL_VERSION(7, 21, 3) CASEPRINT(CURLOPT_RESOLVE); +#endif +#if CURL_VERSION(7, 21, 4) CASEPRINT(CURLOPT_TLSAUTH_USERNAME); CASEPRINT(CURLOPT_TLSAUTH_PASSWORD); CASEPRINT(CURLOPT_TLSAUTH_TYPE); +#endif +#if CURL_VERSION(7, 21, 6) CASEPRINT(CURLOPT_TRANSFER_ENCODING); +#endif +#if CURL_VERSION(7, 21, 7) CASEPRINT(CURLOPT_CLOSESOCKETFUNCTION); CASEPRINT(CURLOPT_CLOSESOCKETDATA); +#endif +#if CURL_VERSION(7, 22, 0) CASEPRINT(CURLOPT_GSSAPI_DELEGATION); +#endif +#if CURL_VERSION(7, 24, 0) CASEPRINT(CURLOPT_DNS_SERVERS); CASEPRINT(CURLOPT_ACCEPTTIMEOUT_MS); +#endif +#if CURL_VERSION(7, 25, 0) CASEPRINT(CURLOPT_TCP_KEEPALIVE); CASEPRINT(CURLOPT_TCP_KEEPIDLE); CASEPRINT(CURLOPT_TCP_KEEPINTVL); +#endif CASEPRINT(CURLOPT_SSL_OPTIONS); +#if CURL_VERSION(7, 25, 0) CASEPRINT(CURLOPT_MAIL_AUTH); +#endif default: os << ""; } From 93416e40a44e6b9fdcab040235394802d4f849c9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 7 Aug 2012 05:16:59 +0200 Subject: [PATCH 071/123] Compile fix: CURLOPT_SSL_OPTIONS was also added since 7.25.0 --- indra/cwdebug/debug_libcurl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/cwdebug/debug_libcurl.cc b/indra/cwdebug/debug_libcurl.cc index 0858ded92..05df113c8 100644 --- a/indra/cwdebug/debug_libcurl.cc +++ b/indra/cwdebug/debug_libcurl.cc @@ -434,8 +434,8 @@ std::ostream& operator<<(std::ostream& os, CURLoption option) CASEPRINT(CURLOPT_TCP_KEEPIDLE); CASEPRINT(CURLOPT_TCP_KEEPINTVL); #endif - CASEPRINT(CURLOPT_SSL_OPTIONS); #if CURL_VERSION(7, 25, 0) + CASEPRINT(CURLOPT_SSL_OPTIONS); CASEPRINT(CURLOPT_MAIL_AUTH); #endif default: From 2fbf6b732eb2cbe1d73f94af8e9f18adac8aae4f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 7 Aug 2012 05:18:24 +0200 Subject: [PATCH 072/123] Fix undefined symbols linker error on linux / relwithdebinfo. See http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Interface.html With this patch, the #pragma will also be used with icc and clang, I didn't test this. --- indra/llcommon/llthread.cpp | 5 +++++ indra/llcommon/llthread.h | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 43fe7596e..dd0efb422 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -29,6 +29,11 @@ * $/LicenseInfo$ */ +#ifdef __GNUC__ +// Generate code for inlines from llthread.h (needed for is_main_thread()). +#pragma implementation "llthread.h" +#endif + #include "linden_common.h" #include "llapr.h" diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index cea8c32c2..ef4303a02 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -33,6 +33,12 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H +#ifdef __GNUC__ +// Needed for is_main_thread() when compiling with optimization (relwithdebinfo). +// It doesn't hurt to just always specify it though. +#pragma interface +#endif + #include "llapp.h" #include "llapr.h" #include "llmemory.h" @@ -308,7 +314,7 @@ private: LLMutexBase* mMutex; }; -class AIRWLock +class LL_COMMON_API AIRWLock { public: AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) : From 5caeccc007b942eb74f27b910a2c5f1914f0847f Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 7 Aug 2012 20:57:39 +0200 Subject: [PATCH 073/123] Final compile/link bug fixes for debug_libcurl code. renamed cwdebug/debug_libcurl.cc -> llmessage/debug_libcurl.cpp and cwdebug/debug_libcurl.h -> llmessage/debug_libcurl.h, because debug_libcurl.cpp does curl calls that do ares and openssl calls, so we need to link with those libraries. llmessage is already linking with those libraries, and contains the main entry point aicurl.h, so it's a suitable place to put this. Bug fix: must always include llpreprocessor.h before including curl/curl.h. Bug fix: Added #include "debug_libcurl.h" to hipporestrequest.cpp and llurlsimstring.cpp which I missed before because they included "curl/curl.h" instead of . Same in llwaterparammanager.cpp, but removed include there because it isn't needed. Now test DEBUG_CURLIO before including debug_curlio, that seems better, because otherwise it would make more sense to replace all #include with #include "mycurl.h" and then do it there-- but I didn't want to do that. Bug fix: we undef-ed CURLOPT_DNS_USE_GLOBAL_CACHE, while really that is an enum, not a macro. Fixed DEBUG_WINDOWS_CODE_ON_LINUX again by adding a hack for ioctlsocket(), not instantiating dumb_socketpair unless DEBUG_WINDOWS_CODE_ON_LINUX is defined and removing again ^M's introduced with the new windows non-blocking code. Also changed the type of flags passed to fcntl to int (was long). --- indra/cmake/Cwdebug.cmake | 2 - indra/llmessage/CMakeLists.txt | 2 + indra/llmessage/aicurl.h | 5 +++ indra/llmessage/aicurlthread.cpp | 41 ++++++++++++------- .../debug_libcurl.cpp} | 7 +++- indra/{cwdebug => llmessage}/debug_libcurl.h | 16 ++++++-- indra/newview/hipporestrequest.cpp | 12 +++--- indra/newview/llurlsimstring.cpp | 5 ++- indra/newview/llwaterparammanager.cpp | 2 - 9 files changed, 62 insertions(+), 30 deletions(-) rename indra/{cwdebug/debug_libcurl.cc => llmessage/debug_libcurl.cpp} (99%) rename indra/{cwdebug => llmessage}/debug_libcurl.h (91%) diff --git a/indra/cmake/Cwdebug.cmake b/indra/cmake/Cwdebug.cmake index f68c7a897..d469386e1 100644 --- a/indra/cmake/Cwdebug.cmake +++ b/indra/cmake/Cwdebug.cmake @@ -2,7 +2,6 @@ include_directories (${CMAKE_SOURCE_DIR}/cwdebug) set(cwdebug_SOURCE_FILES ${CMAKE_SOURCE_DIR}/cwdebug/debug.cc - ${CMAKE_SOURCE_DIR}/cwdebug/debug_libcurl.cc ) set(cwdebug_HEADER_FILES @@ -10,7 +9,6 @@ set(cwdebug_HEADER_FILES ${CMAKE_SOURCE_DIR}/cwdebug/sys.h ${CMAKE_SOURCE_DIR}/cwdebug/debug.h ${CMAKE_SOURCE_DIR}/cwdebug/debug_ostream_operators.h - ${CMAKE_SOURCE_DIR}/cwdebug/debug_libcurl.h ) set_source_files_properties(${cwdebug_HEADER_FILES} diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index b2b8eb4ec..aac669137 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -30,6 +30,7 @@ set(llmessage_SOURCE_FILES llcircuit.cpp llclassifiedflags.cpp aicurl.cpp + debug_libcurl.cpp aicurlthread.cpp lldatapacker.cpp lldispatcher.cpp @@ -117,6 +118,7 @@ set(llmessage_HEADER_FILES llclassifiedflags.h llcurl.h aicurl.h + debug_libcurl.h aicurlprivate.h aicurlthread.h lldatapacker.h diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index 4752d1b45..895650ec8 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -37,11 +37,16 @@ #include #include #include + +#include "llpreprocessor.h" #include // Needed for files that include this header (also for aicurlprivate.h). +#ifdef DEBUG_CURLIO #include "debug_libcurl.h" +#endif // Make sure we don't use this option: it is not thread-safe. #undef CURLOPT_DNS_USE_GLOBAL_CACHE +#define CURLOPT_DNS_USE_GLOBAL_CACHE do_not_use_CURLOPT_DNS_USE_GLOBAL_CACHE #include "stdtypes.h" // U32 #include "lliopipe.h" // LLIOPipe::buffer_ptr_t diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index c03a0f309..4d56a0622 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -177,6 +177,19 @@ int closesocket(curl_socket_t fd) return close(fd); } +int const FIONBIO = 0; + +int ioctlsocket(int fd, int, unsigned long* nonblocking_enable) +{ + int res = fcntl(fd, F_GETFL, 0); + llassert_always(res != -1); + if (*nonblocking_enable) + res |= O_NONBLOCK; + else + res &= ~O_NONBLOCK; + return fcntl(fd, F_SETFD, res); +} + #endif // DEBUG_WINDOWS_CODE_ON_LINUX #define WINDOWS_CODE (LL_WINDOWS || DEBUG_WINDOWS_CODE_ON_LINUX) @@ -894,7 +907,7 @@ static int dumb_socketpair(SOCKET socks[2], bool make_overlapped) WSASetLastError(e); return SOCKET_ERROR; } -#else +#elif WINDOWS_CODE int dumb_socketpair(int socks[2], int dummy) { (void) dummy; @@ -913,18 +926,18 @@ void AICurlThread::create_wakeup_fds(void) llerrs << "Failed to generate wake-up socket pair" << formatWSAError() << llendl; return; } - u_long nonblocking_enable = TRUE; - int error = ioctlsocket(socks[0], FIONBIO, &nonblocking_enable); - if(error) - { - llerrs << "Failed to set wake-up socket nonblocking: " << formatWSAError() << llendl; - } - llassert(nonblocking_enable); - error = ioctlsocket(socks[1], FIONBIO, &nonblocking_enable); - if(error) - { - llerrs << "Failed to set wake-up input socket nonblocking: " << formatWSAError() << llendl; - } + u_long nonblocking_enable = TRUE; + int error = ioctlsocket(socks[0], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up socket nonblocking: " << formatWSAError() << llendl; + } + llassert(nonblocking_enable); + error = ioctlsocket(socks[1], FIONBIO, &nonblocking_enable); + if(error) + { + llerrs << "Failed to set wake-up input socket nonblocking: " << formatWSAError() << llendl; + } mWakeUpFd = socks[0]; mWakeUpFd_in = socks[1]; #else @@ -933,7 +946,7 @@ void AICurlThread::create_wakeup_fds(void) { llerrs << "Failed to create wakeup pipe: " << strerror(errno) << llendl; } - long flags = O_NONBLOCK; + int const flags = O_NONBLOCK; for (int i = 0; i < 2; ++i) { if (fcntl(pipefd[i], F_SETFL, flags)) diff --git a/indra/cwdebug/debug_libcurl.cc b/indra/llmessage/debug_libcurl.cpp similarity index 99% rename from indra/cwdebug/debug_libcurl.cc rename to indra/llmessage/debug_libcurl.cpp index 05df113c8..b2bdd6f64 100644 --- a/indra/cwdebug/debug_libcurl.cc +++ b/indra/llmessage/debug_libcurl.cpp @@ -4,6 +4,8 @@ #include #include #include +#include "llpreprocessor.h" +#include #define COMPILING_DEBUG_LIBCURL_CC #include "debug_libcurl.h" #include "../llcommon/llerror.h" @@ -801,6 +803,7 @@ char* debug_curl_version(void) } -#else +#else // DEBUG_CURLIO int debug_libcurl_dummy; // I thought some OS didn't like empty source files. -#endif +#endif // DEBUG_CURLIO + diff --git a/indra/cwdebug/debug_libcurl.h b/indra/llmessage/debug_libcurl.h similarity index 91% rename from indra/cwdebug/debug_libcurl.h rename to indra/llmessage/debug_libcurl.h index bcf15e0f0..98884f077 100644 --- a/indra/cwdebug/debug_libcurl.h +++ b/indra/llmessage/debug_libcurl.h @@ -1,9 +1,19 @@ #ifndef DEBUG_LIBCURL #define DEBUG_LIBCURL -#ifdef DEBUG_CURLIO +#ifndef DEBUG_CURLIO +#error "Don't include debug_libcurl.h unless DEBUG_CURLIO is defined." +#endif + +#ifndef CURLINFO_TYPEMASK +#error " must be included before including debug_libcurl.h!" +#endif + +#ifndef LLPREPROCESSOR_H +// CURL_STATICLIB is needed on windows namely, which is defined in llpreprocessor.h (but only on windows). +#error "llpreprocessor.h must be included before ." +#endif -#include #include "debug.h" extern "C" { @@ -78,6 +88,4 @@ extern CWD_API char* debug_curl_version(void); #endif // !COMPILING_DEBUG_LIBCURL_CC -#endif // DEBUG_CURLIO - #endif // DEBUG_LIBCURL diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index 4cd0cc34c..60d7770a1 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -8,14 +8,16 @@ #endif #include -#include -#include -#include -#include -#include +#include "llbufferstream.h" +#include "llerror.h" +#include "llhttpclient.h" +#include "llurlrequest.h" +#include "llxmltree.h" #include +#ifdef DEBUG_CURLIO #include "debug_libcurl.h" +#endif // ******************************************************************** diff --git a/indra/newview/llurlsimstring.cpp b/indra/newview/llurlsimstring.cpp index a28988db5..200345cae 100644 --- a/indra/newview/llurlsimstring.cpp +++ b/indra/newview/llurlsimstring.cpp @@ -38,7 +38,10 @@ #include "llpanellogin.h" #include "llviewercontrol.h" -#include "curl/curl.h" +#include // curl_unescape, curl_free +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif //static LLURLSimString LLURLSimString::sInstance; diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index ab95ee14d..307edffdd 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -76,8 +76,6 @@ #include "llagentcamera.h" -#include "curl/curl.h" - LLWaterParamManager::LLWaterParamManager() : mFogColor(22.f/255.f, 43.f/255.f, 54.f/255.f, 0.0f, 0.0f, "waterFogColor", "WaterFogColor"), mFogDensity(4, "waterFogDensity", 2), From b84f470fcaf70f0c0134a8ad7ad0177c07ffa6d0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 8 Aug 2012 00:40:01 +0200 Subject: [PATCH 074/123] Bug fix: curl_off_t has a different size than void* on 32 bit. --- indra/llmessage/debug_libcurl.cpp | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index b2bdd6f64..750187d38 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -609,11 +609,24 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) void* ptr; curl_off_t offset; } param; + unsigned int param_type = (option / 10000) * 10000; va_start(ap, option); - param.ptr = va_arg(ap, void*); + switch (param_type) + { + case CURLOPTTYPE_LONG: + param.along = va_arg(ap, long); + break; + case CURLOPTTYPE_OBJECTPOINT: + case CURLOPTTYPE_FUNCTIONPOINT: + param.ptr = va_arg(ap, void*); + break; + case CURLOPTTYPE_OFF_T: + param.offset = va_arg(ap, curl_off_t); + break; + } va_end(ap); ret = curl_easy_setopt(handle, option, param.ptr); - switch ((option / 10000) * 10000) + switch (param_type) { case CURLOPTTYPE_LONG: Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret); @@ -732,11 +745,24 @@ CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) void* ptr; curl_off_t offset; } param; + unsigned int param_type = (option / 10000) * 10000; va_start(ap, option); - param.ptr = va_arg(ap, void*); + switch (param_type) + { + case CURLOPTTYPE_LONG: + param.along = va_arg(ap, long); + break; + case CURLOPTTYPE_OBJECTPOINT: + case CURLOPTTYPE_FUNCTIONPOINT: + param.ptr = va_arg(ap, void*); + break; + case CURLOPTTYPE_OFF_T: + param.offset = va_arg(ap, curl_off_t); + break; + } va_end(ap); ret = curl_multi_setopt(multi_handle, option, param.ptr); - switch ((option / 10000) * 10000) + switch (param_type) { case CURLOPTTYPE_LONG: Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret); From 80eb0851d9cc8fbb98d82fee7b20d4c041ca20ff Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 8 Aug 2012 01:02:08 +0200 Subject: [PATCH 075/123] Fix stupidity --- indra/llmessage/debug_libcurl.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index 750187d38..d1abc3837 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -625,19 +625,22 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) break; } va_end(ap); - ret = curl_easy_setopt(handle, option, param.ptr); switch (param_type) { case CURLOPTTYPE_LONG: + ret = curl_easy_setopt(handle, option, param.along); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret); break; case CURLOPTTYPE_OBJECTPOINT: + ret = curl_easy_setopt(handle, option, param.ptr); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_FUNCTIONPOINT: + ret = curl_easy_setopt(handle, option, param.ptr); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_OFF_T: + ret = curl_easy_setopt(handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); break; } @@ -761,19 +764,22 @@ CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) break; } va_end(ap); - ret = curl_multi_setopt(multi_handle, option, param.ptr); switch (param_type) { case CURLOPTTYPE_LONG: + ret = curl_multi_setopt(multi_handle, option, param.along); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", " << param.along << "L) = " << ret); break; case CURLOPTTYPE_OBJECTPOINT: + ret = curl_multi_setopt(multi_handle, option, param.ptr); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_FUNCTIONPOINT: + ret = curl_multi_setopt(multi_handle, option, param.ptr); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_OFF_T: + ret = curl_easy_setopt(handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); break; } From ccd135b2a650ee47825bb48b2930ce4bafd37f57 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 8 Aug 2012 01:04:27 +0200 Subject: [PATCH 076/123] and again --- indra/llmessage/debug_libcurl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index d1abc3837..6d586f8c5 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -779,7 +779,7 @@ CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_OFF_T: - ret = curl_easy_setopt(handle, option, param.offset); + ret = curl_multi_setopt(multi_handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); break; } From 89ef06fd3fea5de3626db245a79c45000f80d3e7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 8 Aug 2012 01:58:49 +0200 Subject: [PATCH 077/123] Compiler warning fixes. Take into account that debug_libcurl.cpp is no longer linked dynamically. --- indra/llmessage/debug_libcurl.cpp | 14 +++++++- indra/llmessage/debug_libcurl.h | 58 +++++++++++++++---------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index 6d586f8c5..a60e9f0e9 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -3,12 +3,14 @@ #include "sys.h" #include #include +#include #include #include "llpreprocessor.h" #include #define COMPILING_DEBUG_LIBCURL_CC #include "debug_libcurl.h" -#include "../llcommon/llerror.h" +#include "debug.h" +#include "llerror.h" #define CURL_VERSION(major, minor, patch) \ (LIBCURL_VERSION_MAJOR > major || \ @@ -623,6 +625,9 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) case CURLOPTTYPE_OFF_T: param.offset = va_arg(ap, curl_off_t); break; + default: + std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl; + std::exit(EXIT_FAILURE); } va_end(ap); switch (param_type) @@ -643,6 +648,8 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) ret = curl_easy_setopt(handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); break; + default: + break; } return ret; } @@ -762,6 +769,9 @@ CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) case CURLOPTTYPE_OFF_T: param.offset = va_arg(ap, curl_off_t); break; + default: + std::cerr << "Extracting param_type failed; option = " << option << "; param_type = " << param_type << std::endl; + std::exit(EXIT_FAILURE); } va_end(ap); switch (param_type) @@ -782,6 +792,8 @@ CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...) ret = curl_multi_setopt(multi_handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURLM*)multi_handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); break; + default: // Stop compiler complaining about no default. + break; } return ret; } diff --git a/indra/llmessage/debug_libcurl.h b/indra/llmessage/debug_libcurl.h index 98884f077..27becb1c6 100644 --- a/indra/llmessage/debug_libcurl.h +++ b/indra/llmessage/debug_libcurl.h @@ -14,38 +14,36 @@ #error "llpreprocessor.h must be included before ." #endif -#include "debug.h" - extern "C" { -extern CWD_API void debug_curl_easy_cleanup(CURL* handle); -extern CWD_API CURL* debug_curl_easy_duphandle(CURL* handle); -extern CWD_API char* debug_curl_easy_escape(CURL* curl, char* url, int length); -extern CWD_API CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...); -extern CWD_API CURL* debug_curl_easy_init(void); -extern CWD_API CURLcode debug_curl_easy_pause(CURL* handle, int bitmask); -extern CWD_API CURLcode debug_curl_easy_perform(CURL* handle); -extern CWD_API void debug_curl_easy_reset(CURL* handle); -extern CWD_API CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...); -extern CWD_API char const* debug_curl_easy_strerror(CURLcode errornum); -extern CWD_API char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength); -extern CWD_API void debug_curl_free(char* ptr); -extern CWD_API time_t debug_curl_getdate(char const* datestring, time_t* now); -extern CWD_API void debug_curl_global_cleanup(void); -extern CWD_API CURLcode debug_curl_global_init(long flags); -extern CWD_API CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle); -extern CWD_API CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr); -extern CWD_API CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle); -extern CWD_API CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue); -extern CWD_API CURLM* debug_curl_multi_init(void); -extern CWD_API CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle); -extern CWD_API CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...); -extern CWD_API CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles); -extern CWD_API char const* debug_curl_multi_strerror(CURLMcode errornum); -extern CWD_API struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string); -extern CWD_API void debug_curl_slist_free_all(struct curl_slist* list); -extern CWD_API char* debug_curl_unescape(char const* url, int length); -extern CWD_API char* debug_curl_version(void); +extern void debug_curl_easy_cleanup(CURL* handle); +extern CURL* debug_curl_easy_duphandle(CURL* handle); +extern char* debug_curl_easy_escape(CURL* curl, char* url, int length); +extern CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...); +extern CURL* debug_curl_easy_init(void); +extern CURLcode debug_curl_easy_pause(CURL* handle, int bitmask); +extern CURLcode debug_curl_easy_perform(CURL* handle); +extern void debug_curl_easy_reset(CURL* handle); +extern CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...); +extern char const* debug_curl_easy_strerror(CURLcode errornum); +extern char* debug_curl_easy_unescape(CURL* curl, char* url, int inlength, int* outlength); +extern void debug_curl_free(char* ptr); +extern time_t debug_curl_getdate(char const* datestring, time_t* now); +extern void debug_curl_global_cleanup(void); +extern CURLcode debug_curl_global_init(long flags); +extern CURLMcode debug_curl_multi_add_handle(CURLM* multi_handle, CURL* easy_handle); +extern CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, void* sockptr); +extern CURLMcode debug_curl_multi_cleanup(CURLM* multi_handle); +extern CURLMsg* debug_curl_multi_info_read(CURLM* multi_handle, int* msgs_in_queue); +extern CURLM* debug_curl_multi_init(void); +extern CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle); +extern CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...); +extern CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sockfd, int ev_bitmask, int* running_handles); +extern char const* debug_curl_multi_strerror(CURLMcode errornum); +extern struct curl_slist* debug_curl_slist_append(struct curl_slist* list, char const* string); +extern void debug_curl_slist_free_all(struct curl_slist* list); +extern char* debug_curl_unescape(char const* url, int length); +extern char* debug_curl_version(void); } From f94f458922cafa487ce388a05a7379de3de55f8c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 8 Aug 2012 04:10:34 +0200 Subject: [PATCH 078/123] Print CURL_SOCKET_TIMEOUT for that particular value. --- indra/llmessage/debug_libcurl.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index a60e9f0e9..c981ef38f 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -480,6 +480,20 @@ std::ostream& operator<<(std::ostream& os, CURLMsg* msg) return os; } +struct Socket { + curl_socket_t mSocket; + Socket(curl_socket_t sockfd) : mSocket(sockfd) { } +}; + +std::ostream& operator<<(std::ostream& os, Socket const& sock) +{ + if (sock.mSocket == CURL_SOCKET_TIMEOUT) + os << "CURL_SOCKET_TIMEOUT"; + else + os << sock.mSocket; + return os; +} + struct EvBitmask { int mBitmask; EvBitmask(int mask) : mBitmask(mask) { } @@ -710,7 +724,7 @@ CURLMcode debug_curl_multi_assign(CURLM* multi_handle, curl_socket_t sockfd, voi { CURLMcode ret; ret = curl_multi_assign(multi_handle, sockfd, sockptr); - Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << sockfd << ", " << sockptr << ") = " << ret); + Dout(dc::curl, "curl_multi_assign(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << ", " << sockptr << ") = " << ret); return ret; } @@ -802,7 +816,7 @@ CURLMcode debug_curl_multi_socket_action(CURLM* multi_handle, curl_socket_t sock { CURLMcode ret; ret = curl_multi_socket_action(multi_handle, sockfd, ev_bitmask, running_handles); - Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << sockfd << + Dout(dc::curl, "curl_multi_socket_action(" << (AICURLM*)multi_handle << ", " << Socket(sockfd) << ", " << EvBitmask(ev_bitmask) << ", {" << (ret == CURLM_OK ? *running_handles : 0) << "}) = " << ret); return ret; } From 37c8ea54ebfe28b0d84756b88fdf97600a096ea4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 9 Aug 2012 06:30:31 +0200 Subject: [PATCH 079/123] Add AIThreadID - Cleanup of apr_os_thread* related code. Apart from just really cleaning things up and moving everything into one class regarding thread IDs (ie, is_main_thread(), comparing ID's etc), this also fixes an obscure bug where LL was casting thread ID's to U32 and then compared those to find out if it the same thread. It's theoretically possible that such fails on a 64bit OS. By generalizing the interface, I adopted the use of a thread-local cache for the current thread ID as used by LLMutex et al, so now all code benefits from that. The idea was even extended to now also be used for is_main_thread() tests and even resetting a thread ID to the ID of the current thread. --- indra/cwdebug/debug.h | 15 +-- indra/llcommon/CMakeLists.txt | 8 +- indra/llcommon/aithreadid.cpp | 70 ++++++++++++++ indra/llcommon/aithreadid.h | 91 +++++++++++++++++++ indra/llcommon/aithreadsafe.h | 6 +- indra/llcommon/llaprpool.cpp | 6 +- indra/llcommon/llaprpool.h | 16 ++-- indra/llcommon/llerror.h | 4 - indra/llcommon/llframetimer.h | 3 + indra/llcommon/llthread.cpp | 83 ++++++++--------- indra/llcommon/llthread.h | 38 ++------ indra/newview/statemachine/aistatemachine.cpp | 10 +- indra/newview/statemachine/aistatemachine.h | 6 +- 13 files changed, 246 insertions(+), 110 deletions(-) create mode 100644 indra/llcommon/aithreadid.cpp create mode 100644 indra/llcommon/aithreadid.h diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 4161e18f7..b704c811d 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -49,6 +49,7 @@ #else // LL_COMMON_LINK_SHARED #error LL_COMMON_LINK_SHARED not defined #endif // LL_COMMON_LINK_SHARED +#include "aithreadid.h" // If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace // some of the cwd macro's with something that generates viewer @@ -104,23 +105,23 @@ struct fake_channel { void on() const { } void off() const { } }; -extern CWD_API fake_channel const warning; -extern CWD_API fake_channel const curl; -extern CWD_API fake_channel const curlio; -extern CWD_API fake_channel const statemachine; -extern CWD_API fake_channel const notice; +extern LL_COMMON_API fake_channel const warning; +extern LL_COMMON_API fake_channel const curl; +extern LL_COMMON_API fake_channel const curlio; +extern LL_COMMON_API fake_channel const statemachine; +extern LL_COMMON_API fake_channel const notice; } // namespace dc } // namespace debug #define Debug(x) do { using namespace debug; x; } while(0) -#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) #define DoutEntering(a, b) \ int __slviewer_debug_indentation = 2; \ { \ using namespace debug; \ if ((a).mOn) \ - llinfos_nf << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ + llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ else \ __slviewer_debug_indentation = 0; \ } \ diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 4be690734..cf1e564ae 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -15,8 +15,9 @@ include_directories( ) set(llcommon_SOURCE_FILES - aiframetimer.cpp - imageids.cpp + aiframetimer.cpp + aithreadid.cpp + imageids.cpp indra_constants.cpp llallocator.cpp llallocator_heap_profile.cpp @@ -24,7 +25,7 @@ set(llcommon_SOURCE_FILES llapr.cpp llaprpool.cpp llassettype.cpp - llavatarname.cpp + llavatarname.cpp llbase32.cpp llbase64.cpp llcommon.cpp @@ -103,6 +104,7 @@ set(llcommon_HEADER_FILES CMakeLists.txt aiframetimer.h + aithreadid.h aithreadsafe.h bitpack.h ctype_workaround.h diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp new file mode 100644 index 000000000..dfb4a2660 --- /dev/null +++ b/indra/llcommon/aithreadid.cpp @@ -0,0 +1,70 @@ +/** + * @file aithreadid.cpp + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 08/08/2012 + * - Initial version, written by Aleric Inglewood @ SL + */ + +#include "sys.h" +#include +#include +#include "aithreadid.h" + +AIThreadID const AIThreadID::sNone(AIThreadID::none); +apr_os_thread_t AIThreadID::sMainThreadID; +#ifndef LL_DARWIN +apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread = AIThreadID::undefinedID; +#endif + +void AIThreadID::set_main_thread_id(void) +{ + sMainThreadID = apr_os_thread_current(); +} + +void AIThreadID::set_current_thread_id(void) +{ +#ifndef LL_DARWIN + lCurrentThread = apr_os_thread_current(); +#endif +} + +std::ostream& operator<<(std::ostream& os, AIThreadID const& id) +{ + return os << id.mID; +} + +std::ostream& operator<<(std::ostream& os, AIThreadID::dout_print_t) +{ + if (!AIThreadID::in_main_thread()) + { +#ifdef LL_DARWIN + os << std::hex << (size_t)apr_os_thread_current() << std::dec << ' '; +#else + os << std::hex << (size_t)AIThreadID::lCurrentThread << std::dec << ' '; +#endif + } + return os; +} + diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h new file mode 100644 index 000000000..c565b5913 --- /dev/null +++ b/indra/llcommon/aithreadid.h @@ -0,0 +1,91 @@ +/** + * @file aithreadid.h + * @brief Declaration of AIThreadID. + * + * Copyright (c) 2012, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 08/08/2012 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AI_THREAD_ID +#define AI_THREAD_ID + +#include // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal(). +#include // std::ostream. +#include "llpreprocessor.h" // LL_COMMON_API +#include "llerror.h" + +#if LL_WINDOWS +#define ll_thread_local __declspec(thread) +#else +#define ll_thread_local __thread +#endif + +// Lightweight wrapper around apr_os_thread_t. +// This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety. +class LL_COMMON_API AIThreadID +{ +private: + apr_os_thread_t mID; + static apr_os_thread_t sMainThreadID; + static apr_os_thread_t const undefinedID = (apr_os_thread_t)-1; +#ifndef LL_DARWIN + static ll_thread_local apr_os_thread_t lCurrentThread; +#endif +public: + static AIThreadID const sNone; + enum undefined_thread_t { none }; + enum dout_print_t { DoutPrint }; + +public: + AIThreadID(void) : mID(apr_os_thread_current()) { } + explicit AIThreadID(undefined_thread_t) : mID(undefinedID) { } // Used for sNone. + AIThreadID(AIThreadID const& id) : mID(id.mID) { } + AIThreadID& operator=(AIThreadID const& id) { mID = id.mID; return *this; } + bool is_main_thread(void) const { return apr_os_thread_equal(mID, sMainThreadID); } + bool is_no_thread(void) const { return apr_os_thread_equal(mID, sNone.mID); } + friend LL_COMMON_API bool operator==(AIThreadID const& id1, AIThreadID const& id2) { return apr_os_thread_equal(id1.mID, id2.mID); } + friend LL_COMMON_API bool operator!=(AIThreadID const& id1, AIThreadID const& id2) { return !apr_os_thread_equal(id1.mID, id2.mID); } + friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id); + friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, dout_print_t); + static void set_main_thread_id(void); // Called once to set sMainThreadID. + static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread. +#ifdef LL_DARWIN + void reset(void) { mID = apr_os_thread_current(); } + bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } + static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } +#else + void reset(void) { mID = lCurrentThread; } + bool equals_current_thread(void) const { return apr_os_thread_equal(mID, lCurrentThread); } + static bool in_main_thread(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); } +#endif +}; + +// Legacy function. +inline bool is_main_thread(void) +{ + return AIThreadID::in_main_thread(); +} + +#endif // AI_THREAD_ID diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index b7d0045cb..02bfa8cd7 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -673,18 +673,18 @@ protected: #ifdef LL_DEBUG mutable bool mAccessed; - mutable apr_os_thread_t mTheadID; + mutable AIThreadID mTheadID; void accessed(void) const { if (!mAccessed) { mAccessed = true; - mTheadID = apr_os_thread_current(); + mTheadID.reset(); } else { - llassert_always(apr_os_thread_equal(mTheadID, apr_os_thread_current())); + llassert_always(mTheadID.equals_current_thread()); } } #endif diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp index 3559ff430..de5761157 100644 --- a/indra/llcommon/llaprpool.cpp +++ b/indra/llcommon/llaprpool.cpp @@ -60,10 +60,10 @@ void LLAPRPool::create(LLAPRPool& parent) // // In other words, it's safe for any thread to create a (sub)pool, independent of who // owns the parent pool. - mOwner = apr_os_thread_current(); + mOwner.reset(); #else mOwner = mParent->mOwner; - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); #endif apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool); llassert_always(apr_pool_create_status == APR_SUCCESS); @@ -83,7 +83,7 @@ void LLAPRPool::destroy(void) // of course. Otherwise, if we are a subpool, only the thread that owns // the parent may destruct us, since that is the pool that is still alive, // possibly being used by others and being altered here. - llassert(!mParent || apr_os_thread_equal(mParent->mOwner, apr_os_thread_current())); + llassert(!mParent || mParent->mOwner.equals_current_thread()); #endif apr_pool_t* pool = mPool; mPool = NULL; // Mark that we are BEING destructed. diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h index dc123e942..4c9f36893 100644 --- a/indra/llcommon/llaprpool.h +++ b/indra/llcommon/llaprpool.h @@ -62,22 +62,22 @@ class LL_COMMON_API LLAPRPool protected: apr_pool_t* mPool; //!< Pointer to the underlaying pool. NULL if not initialized. LLAPRPool* mParent; //!< Pointer to the parent pool, if any. Only valid when mPool is non-zero. - apr_os_thread_t mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero. + AIThreadID mOwner; //!< The thread that owns this memory pool. Only valid when mPool is non-zero. public: //! Construct an uninitialized (destructed) pool. - LLAPRPool(void) : mPool(NULL) { } + LLAPRPool(void) : mPool(NULL), mOwner(AIThreadID::none) { } //! Construct a subpool from an existing pool. // This is not a copy-constructor, this class doesn't have one! - LLAPRPool(LLAPRPool& parent) : mPool(NULL) { create(parent); } + LLAPRPool(LLAPRPool& parent) : mPool(NULL), mOwner(AIThreadID::none) { create(parent); } //! Destruct the memory pool (free all of it's subpools and allocated memory). ~LLAPRPool() { destroy(); } protected: // Create a pool that is allocated from the Operating System. Only used by LLAPRRootPool. - LLAPRPool(int) : mPool(NULL), mParent(NULL), mOwner(apr_os_thread_current()) + LLAPRPool(int) : mPool(NULL), mParent(NULL) { apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, NULL); llassert_always(apr_pool_create_status == APR_SUCCESS); @@ -104,7 +104,7 @@ public: apr_pool_t* operator()(void) const { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return mPool; } @@ -112,7 +112,7 @@ public: void clear(void) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); apr_pool_clear(mPool); } @@ -124,13 +124,13 @@ public: void* palloc(size_t size) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return apr_palloc(mPool, size); } void* pcalloc(size_t size) { llassert(mPool); - llassert(apr_os_thread_equal(mOwner, apr_os_thread_current())); + llassert(mOwner.equals_current_thread()); return apr_pcalloc(mPool, size); } #endif diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index d0279f866..c4865e947 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -308,8 +308,4 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; Such computation is done iff the message will be logged. */ -#ifdef SHOW_ASSERT -extern LL_COMMON_API bool is_main_thread(); -#endif - #endif // LL_LLERROR_H diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index bae3e5615..2bb1943c8 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -44,6 +44,9 @@ #include "lltimer.h" #include "timing.h" #include +#ifdef SHOW_ASSERT +#include "aithreadid.h" // is_main_thread() +#endif class LL_COMMON_API LLFrameTimer { diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index dd0efb422..c8d0be2f4 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -67,18 +67,12 @@ // //---------------------------------------------------------------------------- -#if !LL_DARWIN -U32 ll_thread_local local_thread_ID = 0; -#endif - -U32 LLThread::sIDIter = 0; LLAtomicS32 LLThread::sCount = 0; LLAtomicS32 LLThread::sRunning = 0; LL_COMMON_API void assert_main_thread() { - static U32 s_thread_id = LLThread::currentID(); - if (LLThread::currentID() != s_thread_id) + if (!AIThreadID::in_main_thread()) { llerrs << "Illegal execution outside main thread." << llendl; } @@ -95,9 +89,8 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap LLThread *threadp = (LLThread *)datap; -#if !LL_DARWIN - local_thread_ID = threadp->mID; -#endif + // Initialize thread-local cache of current thread ID (if supported). + AIThreadID::set_current_thread_id(); // Create a thread local data. LLThreadLocalData::create(threadp); @@ -137,7 +130,6 @@ LLThread::LLThread(std::string const& name) : mStatus(STOPPED), mThreadLocalData(NULL) { - mID = ++sIDIter; sCount++; llassert(sCount <= 50); mRunCondition = new LLCondition; @@ -281,12 +273,6 @@ void LLThread::setQuitting() wake(); } -// static -U32 LLThread::currentID() -{ - return (U32)apr_os_thread_current(); -} - // static void LLThread::yield() { @@ -315,14 +301,6 @@ void LLThread::wakeLocked() } } -//static -apr_os_thread_t LLThread::sMainThreadID; - -void LLThread::set_main_thread_id(void) -{ - sMainThreadID = apr_os_thread_current(); -} - // The thread private handle to access the LLThreadLocalData instance. apr_threadkey_t* LLThreadLocalData::sThreadLocalDataKey; @@ -345,6 +323,10 @@ void LLThreadLocalData::init(void) return; } + // This function is called by the main thread (these values are also needed in the next line). + AIThreadID::set_main_thread_id(); + AIThreadID::set_current_thread_id(); + apr_status_t status = apr_threadkey_private_create(&sThreadLocalDataKey, &LLThreadLocalData::destroy, LLAPRRootPool::get()()); ll_apr_assert_status(status); // Or out of memory, or system-imposed limit on the // total number of keys per process {PTHREAD_KEYS_MAX} @@ -352,9 +334,6 @@ void LLThreadLocalData::init(void) // Create the thread-local data for the main thread (this function is called by the main thread). LLThreadLocalData::create(NULL); - - // This function is called by the main thread. - LLThread::set_main_thread_id(); } // This is called once for every thread when the thread is destructed. @@ -421,27 +400,19 @@ void LLCondition::broadcast() //============================================================================ LLMutexBase::LLMutexBase() : - mLockingThread(NO_THREAD), + mLockingThread(AIThreadID::sNone), mCount(0) { } bool LLMutexBase::isSelfLocked() const { -#if LL_DARWIN - return mLockingThread == LLThread::currentID(); -#else - return mLockingThread == local_thread_ID; -#endif + return mLockingThread.equals_current_thread(); } void LLMutexBase::lock() { -#if LL_DARWIN - if (mLockingThread == LLThread::currentID()) -#else - if (mLockingThread == local_thread_ID) -#endif + if (mLockingThread.equals_current_thread()) { //redundant lock mCount++; return; @@ -449,11 +420,33 @@ void LLMutexBase::lock() apr_thread_mutex_lock(mAPRMutexp); -#if LL_DARWIN - mLockingThread = LLThread::currentID(); -#else - mLockingThread = local_thread_ID; -#endif + mLockingThread.reset(); +} + +bool LLMutexBase::tryLock() +{ + if (mLockingThread.equals_current_thread()) + { //redundant lock + mCount++; + return true; + } + bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); + if (success) + { + mLockingThread.reset(); + } + return success; +} + +// non-blocking, but does do a lock/unlock so not free +bool LLMutexBase::isLocked() const +{ + if (mLockingThread.equals_current_thread()) + return false; // A call to lock() won't block. + if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp))) + return true; + apr_thread_mutex_unlock(mAPRMutexp); + return false; } void LLMutexBase::unlock() @@ -463,7 +456,7 @@ void LLMutexBase::unlock() mCount--; return; } - mLockingThread = NO_THREAD; + mLockingThread = AIThreadID::sNone; apr_thread_mutex_unlock(mAPRMutexp); } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index ef4303a02..549a090ec 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -45,17 +45,12 @@ #include "apr_thread_cond.h" #include "llaprpool.h" #include "llatomic.h" +#include "aithreadid.h" class LLThread; class LLMutex; class LLCondition; -#if LL_WINDOWS -#define ll_thread_local __declspec(thread) -#else -#define ll_thread_local __thread -#endif - class LL_COMMON_API LLThreadLocalDataMember { public: @@ -85,10 +80,12 @@ private: ~LLThreadLocalData(); }; +// Print to llerrs if the current thread is not the main thread. +LL_COMMON_API void assert_main_thread(); + class LL_COMMON_API LLThread { private: - static apr_os_thread_t sMainThreadID; static U32 sIDIter; static LLAtomicS32 sCount; static LLAtomicS32 sRunning; @@ -108,11 +105,9 @@ public: bool isQuitting() const { return (QUITTING == mStatus); } bool isStopped() const { return (STOPPED == mStatus); } - static U32 currentID(); // Return ID of current thread static S32 getCount() { return sCount; } static S32 getRunning() { return sRunning; } static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. - static bool is_main_thread(void) { return apr_os_thread_equal(LLThread::sMainThreadID, apr_os_thread_current()); } public: // PAUSE / RESUME functionality. See source code for important usage notes. @@ -136,11 +131,6 @@ public: // Return thread-local data for the current thread. static LLThreadLocalData& tldata(void) { return LLThreadLocalData::tldata(); } - // Called once, from LLThreadLocalData::init(). - static void set_main_thread_id(void); - - U32 getID() const { return mID; } - private: bool mPaused; @@ -153,7 +143,6 @@ protected: apr_thread_t *mAPRThreadp; volatile EThreadStatus mStatus; - U32 mID; friend void LLThreadLocalData::create(LLThread* threadp); LLThreadLocalData* mThreadLocalData; @@ -183,8 +172,7 @@ protected: }; #ifdef SHOW_ASSERT -LL_COMMON_API inline bool is_main_thread(void) { return LLThread::is_main_thread(); } -#define ASSERT_SINGLE_THREAD do { static apr_os_thread_t first_thread_id = apr_os_thread_current(); llassert(apr_os_thread_equal(first_thread_id, apr_os_thread_current())); } while(0) +#define ASSERT_SINGLE_THREAD do { static AIThreadID first_thread_id; llassert(first_thread_id.equals_current_thread()); } while(0) #else #define ASSERT_SINGLE_THREAD do { } while(0) #endif @@ -204,32 +192,24 @@ LL_COMMON_API inline bool is_main_thread(void) { return LLThread::is_main_thread class LL_COMMON_API LLMutexBase { public: - typedef enum - { - NO_THREAD = 0xFFFFFFFF - } e_locking_thread; - LLMutexBase() ; void lock(); // blocks void unlock(); // Returns true if lock was obtained successfully. - bool tryLock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); } + bool tryLock(); - // non-blocking, but does do a lock/unlock so not free - bool isLocked() { bool is_not_locked = tryLock(); if (is_not_locked) unlock(); return !is_not_locked; } + // Returns true if a call to lock() would block (returns false if self-locked()). + bool isLocked() const; // Returns true if locked by this thread. bool isSelfLocked() const; - // get ID of locking thread - U32 lockingThread() const { return mLockingThread; } - protected: // mAPRMutexp is initialized and uninitialized in the derived class. apr_thread_mutex_t* mAPRMutexp; mutable U32 mCount; - mutable U32 mLockingThread; + mutable AIThreadID mLockingThread; private: // Disallow copy construction and assignment. diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index 675317cb1..f008a4873 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -206,7 +206,7 @@ void AIStateMachine::locked_cont(void) // Atomic test mActive and change mIdle. mIdleActive.lock(); #ifdef SHOW_ASSERT - mContThread = apr_os_thread_current(); + mContThread.reset(); #endif mIdle = false; bool not_active = mActive == as_idle; @@ -253,7 +253,7 @@ void AIStateMachine::set_state(state_type state) mSetStateLock.lock(); // Do not call set_state() unless running. - llassert(mState == bs_run || !LLThread::is_main_thread()); + llassert(mState == bs_run || !is_main_thread()); // If this function is called from another thread than the main thread, then we have to ignore // it if we're not idle and the state is less than the current state. The main thread must @@ -279,12 +279,12 @@ void AIStateMachine::set_state(state_type state) // state is less than the current state, ignore it. // Also, if abort() or finish() was called, then we should just ignore it. if (mState != bs_run || - (!mIdle && state <= mRunState && !LLThread::is_main_thread())) + (!mIdle && state <= mRunState && !AIThreadID::in_main_thread())) { #ifdef SHOW_ASSERT // It's a bit weird if the same thread does two calls on a row where the second call // has a smaller value: warn about that. - if (mState == bs_run && mContThread == apr_os_thread_current()) + if (mState == bs_run && mContThread.equals_current_thread()) { llwarns << "Ignoring call to set_state(" << state_str(state) << ") by non-main thread before main-thread could react on previous call, " @@ -296,7 +296,7 @@ void AIStateMachine::set_state(state_type state) } // Do not call idle() when set_state is called from another thread; use idle(state_type) instead. - llassert(!mCalledThreadUnsafeIdle || LLThread::is_main_thread()); + llassert(!mCalledThreadUnsafeIdle || is_main_thread()); // Change mRunState to the requested value. if (mRunState != state) diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 13da6fd3d..55028232b 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -208,7 +208,7 @@ class AIStateMachine { S64 mSleep; //!< Non-zero while the state machine is sleeping. LLMutex mIdleActive; //!< Used for atomic operations on the pair mIdle / mActive. #ifdef SHOW_ASSERT - apr_os_thread_t mContThread; //!< Thread that last called locked_cont(). + AIThreadID mContThread; //!< Thread that last called locked_cont(). bool mCalledThreadUnsafeIdle; //!< Set to true when idle() is called. #endif @@ -242,7 +242,7 @@ class AIStateMachine { //! Create a non-running state machine. AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mActive(as_idle), mSleep(0), mParent(NULL), mCallback(NULL) #ifdef SHOW_ASSERT - , mContThread(0), mCalledThreadUnsafeIdle(false) + , mContThread(AIThreadID::none), mCalledThreadUnsafeIdle(false) #endif { updateSettings(); } @@ -269,7 +269,7 @@ class AIStateMachine { mSetStateLock.lock(); // Ignore calls to cont() if the statemachine isn't idle. See comments in set_state(). // Calling cont() twice or after calling set_state(), without first calling idle(), is an error. - if (mState != bs_run || !mIdle) { llassert(mState != bs_run || mContThread != apr_os_thread_current()); mSetStateLock.unlock(); return; } + if (mState != bs_run || !mIdle) { llassert(mState != bs_run || !mContThread.equals_current_thread()); mSetStateLock.unlock(); return; } locked_cont(); } private: From b9673df961a3aa83f3edf382611ac8c0c55c9c32 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 9 Aug 2012 06:57:53 +0200 Subject: [PATCH 080/123] Clean up of debug.h: just use LL_COMMON_API. --- indra/cwdebug/debug.h | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index b704c811d..f127da767 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -29,26 +29,6 @@ #ifdef DEBUG_CURLIO -#if LL_WINDOWS -#define CWD_DLLEXPORT __declspec(dllexport) -#define CWD_DLLIMPORT __declspec(dllimport) -#elif LL_LINUX -#define CWD_DLLEXPORT __attribute__ ((visibility("default"))) -#define CWD_DLLIMPORT -#else -#define CWD_DLLEXPORT -#define CWD_DLLIMPORT -#endif // LL_WINDOWS - -#if LL_COMMON_LINK_SHARED -#if defined(llcommon_EXPORTS) -#define CWD_API CWD_DLLEXPORT -#else // cwdebug_EXPORTS -#define CWD_API CWD_DLLIMPORT -#endif // cwdebug_EXPORTS -#else // LL_COMMON_LINK_SHARED -#error LL_COMMON_LINK_SHARED not defined -#endif // LL_COMMON_LINK_SHARED #include "aithreadid.h" // If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace @@ -59,10 +39,11 @@ #include #if LL_WINDOWS -#define CWD_API_TLS __declspec(thread) +// On windows, thread-local data is automatically exported. +#define LL_COMMON_API_TLS __declspec(thread) #define CWD_TLS __declspec(thread) #else -#define CWD_API_TLS CWD_API __thread +#define LL_COMMON_API_TLS LL_COMMON_API __thread #define CWD_TLS __thread #endif @@ -81,17 +62,17 @@ inline void init() { } struct libcwd_do_type { void on() const { } }; -extern CWD_API libcwd_do_type const libcw_do; +extern LL_COMMON_API libcwd_do_type const libcw_do; struct Indent { int M_indent; - static CWD_API_TLS int S_indentation; - enum CWD_API print_nt { print }; - CWD_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } - CWD_API ~Indent() { S_indentation -= M_indent; } + static LL_COMMON_API_TLS int S_indentation; + enum LL_COMMON_API print_nt { print }; + LL_COMMON_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } + LL_COMMON_API ~Indent() { S_indentation -= M_indent; } }; -extern CWD_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); -extern CWD_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); namespace dc { From cd197a3807a503b248f1696d2cc78253739ea55d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 9 Aug 2012 07:00:51 +0200 Subject: [PATCH 081/123] Fix LL_REF_COUNT_DEBUG debug code. This code was broken with respect to LLAPRPool. Fix that, and also fixed it for the new AIThreadID API. --- indra/llaudio/llaudiodecodemgr.cpp | 1 + indra/llcommon/llaprpool.h | 1 + indra/llcommon/llmemory.h | 1 - indra/llcommon/llrefcount.cpp | 65 +++++------------------------- indra/llcommon/llrefcount.h | 6 +-- indra/llxml/llcontrol.h | 1 + 6 files changed, 17 insertions(+), 58 deletions(-) diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 5063cbe7a..596a6dcff 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -41,6 +41,7 @@ #include "lldir.h" #include "llendianswizzle.h" #include "llassetstorage.h" +#include "llrefcount.h" #include "vorbis/codec.h" #include "vorbis/vorbisfile.h" diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h index 4c9f36893..74af351e4 100644 --- a/indra/llcommon/llaprpool.h +++ b/indra/llcommon/llaprpool.h @@ -48,6 +48,7 @@ #include "apr_portable.h" #include "apr_pools.h" #include "llerror.h" +#include "aithreadid.h" extern void ll_init_apr(); diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index d01b43917..19cc720b0 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -523,7 +523,6 @@ void LLPrivateMemoryPoolTester::operator delete[](void* addr) //EVENTUALLY REMOVE THESE: #include "llpointer.h" -#include "llrefcount.h" #include "llsingleton.h" #include "llsafehandle.h" diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp index e1876599f..d92735aac 100644 --- a/indra/llcommon/llrefcount.cpp +++ b/indra/llcommon/llrefcount.cpp @@ -38,14 +38,6 @@ LLRefCount::LLRefCount(const LLRefCount& other) : mRef(0) { #if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - mMutexp = new LLMutex(gAPRPoolp) ; - } - else - { - mMutexp = NULL ; - } mCrashAtUnlock = FALSE ; #endif } @@ -60,14 +52,6 @@ LLRefCount::LLRefCount() : mRef(0) { #if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - mMutexp = new LLMutex(gAPRPoolp) ; - } - else - { - mMutexp = NULL ; - } mCrashAtUnlock = FALSE ; #endif } @@ -78,29 +62,20 @@ LLRefCount::~LLRefCount() { llerrs << "deleting non-zero reference" << llendl; } - -#if LL_REF_COUNT_DEBUG - if(gAPRPoolp) - { - delete mMutexp ; - } -#endif } #if LL_REF_COUNT_DEBUG void LLRefCount::ref() const { - if(mMutexp) - { - if(mMutexp->isLocked()) + if(mMutex.isLocked()) { mCrashAtUnlock = TRUE ; llerrs << "the mutex is locked by the thread: " << mLockedThreadID - << " Current thread: " << LLThread::currentID() << llendl ; + << " Current thread: " << AIThreadID() << llendl ; } - mMutexp->lock() ; - mLockedThreadID = LLThread::currentID() ; + mMutex.lock() ; + mLockedThreadID.reset(); mRef++; @@ -108,27 +83,20 @@ void LLRefCount::ref() const { while(1); //crash here. } - mMutexp->unlock() ; - } - else - { - mRef++; - } + mMutex.unlock() ; } S32 LLRefCount::unref() const { - if(mMutexp) - { - if(mMutexp->isLocked()) + if(mMutex.isLocked()) { mCrashAtUnlock = TRUE ; llerrs << "the mutex is locked by the thread: " << mLockedThreadID - << " Current thread: " << LLThread::currentID() << llendl ; + << " Current thread: " << AIThreadID() << llendl ; } - mMutexp->lock() ; - mLockedThreadID = LLThread::currentID() ; + mMutex.lock() ; + mLockedThreadID.reset(); llassert(mRef >= 1); if (0 == --mRef) @@ -137,7 +105,7 @@ S32 LLRefCount::unref() const { while(1); //crash here. } - mMutexp->unlock() ; + mMutex.unlock() ; delete this; return 0; @@ -147,18 +115,7 @@ S32 LLRefCount::unref() const { while(1); //crash here. } - mMutexp->unlock() ; + mMutex.unlock() ; return mRef; - } - else - { - llassert(mRef >= 1); - if (0 == --mRef) - { - delete this; - return 0; - } - return mRef; - } } #endif diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 8eb5d53f3..b7831e7fa 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -30,7 +30,7 @@ #define LL_REF_COUNT_DEBUG 0 #if LL_REF_COUNT_DEBUG -class LLMutex ; +#include "llthread.h" // LLMutexRootPool #endif //---------------------------------------------------------------------------- @@ -80,8 +80,8 @@ private: mutable S32 mRef; #if LL_REF_COUNT_DEBUG - LLMutex* mMutexp ; - mutable U32 mLockedThreadID ; + mutable LLMutexRootPool mMutex ; + mutable AIThreadID mLockedThreadID ; mutable BOOL mCrashAtUnlock ; #endif }; diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 47df59d5e..6ce9745cc 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -42,6 +42,7 @@ #include "v4color.h" #include "v4coloru.h" #include "llinstancetracker.h" +#include "llrefcount.h" #include "llcontrolgroupreader.h" From 76e30150b9abf4523de58969e594f1723dd3206d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 9 Aug 2012 07:02:42 +0200 Subject: [PATCH 082/123] Removed superfluous debug output --- indra/llmessage/aicurlthread.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 4d56a0622..0b1b85c0a 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1011,7 +1011,6 @@ void AICurlThread::wakeup_thread(void) } llassert_always(len == 1); //SGTODO: handle EAGAIN if needed - llinfos << "Sent wakeup signal" << llendl; #else // If write() is interrupted by a signal before it writes any data, it shall return -1 with errno set to [EINTR]. // If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written. From 0054e6a3787bdc33864983a365b54b0f3548daa4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 9 Aug 2012 07:04:23 +0200 Subject: [PATCH 083/123] Bug fix. Fixes an exit crash when the user clicks on Login and immediately on Quit, because termination of the LLAuth curl request statemachine access the CurlRequestTimeOut debug setting. --- indra/newview/llappviewer.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e2c395737..f4153efae 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1752,21 +1752,20 @@ bool LLAppViewer::cleanup() delete gVFS; gVFS = NULL; - // Cleanup settings last in case other clases reference them - gSavedSettings.cleanup(); - gColors.cleanup(); - gCrashSettings.cleanup(); - LLWatchdog::getInstance()->cleanup(); llinfos << "Shutting down message system" << llendflush; end_messaging_system(); llinfos << "Message system deleted." << llendflush; - LLUserAuth::getInstance()->reset(); // Reset before AICurlInterface::cleanupCurl, else LLCURL::sHandleMutex == NULL LLApp::stopErrorThread(); // The following call is not thread-safe. Have to stop all threads. AICurlInterface::cleanupCurl(); + // Cleanup settings last in case other classes reference them. + gSavedSettings.cleanup(); + gColors.cleanup(); + gCrashSettings.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()) From b2c5a84964b84161b846200e94bbf1c872ca94e9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 10 Aug 2012 04:18:04 +0200 Subject: [PATCH 084/123] Replace CURLOPT_POSTFIELDSIZE_LARGE with CURLOPT_POSTFIELDSIZE Also adds a more robust interface for setopt that does type checking based on the options used. This fixes one bug where a F32 was passed and interpreted as long. In many cases a U32 or S32 was passed as long, which would fail (only) on a 64bit non-windows big endian machine. --- indra/llmessage/aicurl.cpp | 74 ++++++++++++++++++- indra/llmessage/aicurlprivate.h | 71 ++++++++++++++---- indra/llmessage/llhttpclient.cpp | 2 +- indra/newview/llxmlrpctransaction.cpp | 2 +- indra/plugins/webkit/linux_volume_catcher.cpp | 4 +- 5 files changed, 133 insertions(+), 20 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 2f90b8e6e..d22408491 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -717,8 +717,78 @@ void intrusive_ptr_release(ThreadSafeCurlEasyRequest* threadsafe_curl_easy_reque } } +CURLcode CurlEasyHandle::setopt(CURLoption option, long parameter) +{ + llassert((CURLOPTTYPE_LONG <= option && option < CURLOPTTYPE_LONG + 1000) || + (sizeof(curl_off_t) == sizeof(long) && + CURLOPTTYPE_OFF_T <= option && option < CURLOPTTYPE_OFF_T + 1000)); + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +// The standard requires that sizeof(long) < sizeof(long long), so it's safe to overload like this. +// We assume that one of them is 64 bit, the size of curl_off_t. +CURLcode CurlEasyHandle::setopt(CURLoption option, long long parameter) +{ + llassert(sizeof(curl_off_t) == sizeof(long long) && + CURLOPTTYPE_OFF_T <= option && option < CURLOPTTYPE_OFF_T + 1000); + llassert(!mActiveMultiHandle); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +CURLcode CurlEasyHandle::setopt(CURLoption option, void const* parameter) +{ + llassert(CURLOPTTYPE_OBJECTPOINT <= option && option < CURLOPTTYPE_OBJECTPOINT + 1000); + setErrorBuffer(); + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); +} + +#define DEFINE_FUNCTION_SETOPT1(function_type, opt1) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +#define DEFINE_FUNCTION_SETOPT3(function_type, opt1, opt2, opt3) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1 || option == opt2 || option == opt3); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +#define DEFINE_FUNCTION_SETOPT4(function_type, opt1, opt2, opt3, opt4) \ + CURLcode CurlEasyHandle::setopt(CURLoption option, function_type parameter) \ + { \ + llassert(option == opt1 || option == opt2 || option == opt3 || option == opt4); \ + setErrorBuffer(); \ + return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); \ + } + +DEFINE_FUNCTION_SETOPT1(curl_debug_callback, CURLOPT_DEBUGFUNCTION) +DEFINE_FUNCTION_SETOPT4(curl_write_callback, CURLOPT_HEADERFUNCTION, CURLOPT_WRITEFUNCTION, CURLOPT_INTERLEAVEFUNCTION, CURLOPT_READFUNCTION) +//DEFINE_FUNCTION_SETOPT1(curl_read_callback, CURLOPT_READFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_ssl_ctx_callback, CURLOPT_SSL_CTX_FUNCTION) +DEFINE_FUNCTION_SETOPT3(curl_conv_callback, CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPT_CONV_FROM_UTF8_FUNCTION) +#if 0 // Not used by the viewer. +DEFINE_FUNCTION_SETOPT1(curl_progress_callback, CURLOPT_PROGRESSFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_seek_callback, CURLOPT_SEEKFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_ioctl_callback, CURLOPT_IOCTLFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_sockopt_callback, CURLOPT_SOCKOPTFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_opensocket_callback, CURLOPT_OPENSOCKETFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_closesocket_callback, CURLOPT_CLOSESOCKETFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_sshkeycallback, CURLOPT_SSH_KEYFUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_chunk_bgn_callback, CURLOPT_CHUNK_BGN_FUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_chunk_end_callback, CURLOPT_CHUNK_END_FUNCTION) +DEFINE_FUNCTION_SETOPT1(curl_fnmatch_callback, CURLOPT_FNMATCH_FUNCTION) +#endif + //----------------------------------------------------------------------------- -// CurlEasyReqest +// CurlEasyRequest void CurlEasyRequest::setoptString(CURLoption option, std::string const& value) { @@ -729,8 +799,8 @@ void CurlEasyRequest::setoptString(CURLoption option, std::string const& value) void CurlEasyRequest::setPost(char const* postdata, S32 size) { setopt(CURLOPT_POST, 1L); - setopt(CURLOPT_POSTFIELDS, static_cast(const_cast(postdata))); setopt(CURLOPT_POSTFIELDSIZE, size); + setopt(CURLOPT_POSTFIELDS, postdata); } ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void) diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 14a8b0fa2..ead30e82e 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -59,6 +59,9 @@ void stopCurlThread(void); class ThreadSafeCurlEasyRequest; class ThreadSafeBufferedCurlEasyRequest; +#define DECLARE_SETOPT(param_type) \ + CURLcode setopt(CURLoption option, param_type parameter) + // This class wraps CURL*'s. // It guarantees that a pointer is cleaned up when no longer needed, as required by libcurl. class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEvents { @@ -75,8 +78,31 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven void reset(void) { llassert(!mActiveMultiHandle); curl_easy_reset(mEasyHandle); } // Set options for a curl easy handle. - template - CURLcode setopt(CURLoption option, BUILTIN parameter); + DECLARE_SETOPT(long); + DECLARE_SETOPT(long long); + DECLARE_SETOPT(void const*); + DECLARE_SETOPT(curl_debug_callback); + DECLARE_SETOPT(curl_write_callback); + //DECLARE_SETOPT(curl_read_callback); Same type as curl_write_callback + DECLARE_SETOPT(curl_ssl_ctx_callback); + DECLARE_SETOPT(curl_conv_callback); +#if 0 // Not used by the viewer. + DECLARE_SETOPT(curl_progress_callback); + DECLARE_SETOPT(curl_seek_callback); + DECLARE_SETOPT(curl_ioctl_callback); + DECLARE_SETOPT(curl_sockopt_callback); + DECLARE_SETOPT(curl_opensocket_callback); + DECLARE_SETOPT(curl_closesocket_callback); + DECLARE_SETOPT(curl_sshkeycallback); + DECLARE_SETOPT(curl_chunk_bgn_callback); + DECLARE_SETOPT(curl_chunk_end_callback); + DECLARE_SETOPT(curl_fnmatch_callback); +#endif +#if __LP64__ // sizeof(long) > sizeof(S32), see http://en.cppreference.com/w/cpp/language/types + // Automatically cast small int types to a long if they differ in size. + CURLcode setopt(CURLoption option, U32 parameter) { return setopt(option, (long)parameter); } + CURLcode setopt(CURLoption option, S32 parameter) { return setopt(option, (long)parameter); } +#endif // Clone a libcurl session handle using all the options previously set. //CurlEasyHandle(CurlEasyHandle const& orig); @@ -160,14 +186,6 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven static char* getTLErrorBuffer(void); }; -template -CURLcode CurlEasyHandle::setopt(CURLoption option, BUILTIN parameter) -{ - llassert(!mActiveMultiHandle); - setErrorBuffer(); - return check_easy_code(curl_easy_setopt(mEasyHandle, option, parameter)); -} - // CurlEasyRequest adds a slightly more powerful interface that can be used // to set the options on a curl easy handle. // @@ -397,16 +415,41 @@ class CurlMultiHandle : public boost::noncopyable { public: // Set options for a curl multi handle. - template - CURLMcode setopt(CURLMoption option, BUILTIN parameter); + CURLMcode setopt(CURLMoption option, long parameter); + CURLMcode setopt(CURLMoption option, curl_socket_callback parameter); + CURLMcode setopt(CURLMoption option, curl_multi_timer_callback parameter); + CURLMcode setopt(CURLMoption option, void* parameter); // Returns total number of existing CURLM* handles (excluding ones created outside this class). static U32 getTotalMultiHandles(void) { return sTotalMultiHandles; } }; -template -CURLMcode CurlMultiHandle::setopt(CURLMoption option, BUILTIN parameter) +// Overload the setopt methods in order to enforce the correct types (ie, convert an int to a long). + +// curl_multi_setopt may only be passed a long, +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, long parameter) { + llassert(option == CURLMOPT_MAXCONNECTS || option == CURLMOPT_PIPELINING); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +// ... or a function pointer, +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_socket_callback parameter) +{ + llassert(option == CURLMOPT_SOCKETFUNCTION); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, curl_multi_timer_callback parameter) +{ + llassert(option == CURLMOPT_TIMERFUNCTION); + return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); +} + +// ... or an object pointer. +inline CURLMcode CurlMultiHandle::setopt(CURLMoption option, void* parameter) +{ + llassert(option == CURLMOPT_SOCKETDATA || option == CURLMOPT_TIMERDATA); return check_multi_code(curl_multi_setopt(mMultiHandle, option, parameter)); } diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 7320ccb0e..9c58bc466 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -446,7 +446,7 @@ static LLSD blocking_request( std::string body_str; // * Set curl handle options - curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, timeout); // seconds, see warning at top of function. + curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function. curlEasyRequest_w->setWriteCallback(&LLHTTPBuffer::curl_write, &http_buffer); // * Setup headers. diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 9d729b8da..b1bb2ee0f 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -263,7 +263,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) if (mRequestText) { Dout(dc::curl, "Writing " << mRequestTextSize << " bytes: \"" << libcwd::buf2str(mRequestText, mRequestTextSize) << "\".");; - curlEasyRequest_w->setopt(CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)mRequestTextSize); + curlEasyRequest_w->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); curlEasyRequest_w->setoptString(CURLOPT_COPYPOSTFIELDS, mRequestText); } else diff --git a/indra/plugins/webkit/linux_volume_catcher.cpp b/indra/plugins/webkit/linux_volume_catcher.cpp index 0cb73ca1e..5298056fd 100644 --- a/indra/plugins/webkit/linux_volume_catcher.cpp +++ b/indra/plugins/webkit/linux_volume_catcher.cpp @@ -36,9 +36,10 @@ */ #include "linden_common.h" -# include +#include #include "volume_catcher.h" +#include "llaprpool.h" #ifndef LL_WINDOWS #include @@ -53,7 +54,6 @@ extern "C" { #include #include // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken. -#include "llaprpool.h" #include "apr_dso.h" #ifdef LL_STANDALONE #include From ffb55f482a9a460c12bcf9362e4c0061820cfd26 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 10 Aug 2012 05:35:11 +0200 Subject: [PATCH 085/123] Compile fixes for windows. --- indra/cwdebug/debug.h | 9 --------- indra/llcommon/aithreadid.cpp | 3 ++- indra/llcommon/aithreadid.h | 18 ++++++------------ indra/llcommon/llpreprocessor.h | 12 ++++++++++++ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index f127da767..9e832a44c 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -38,15 +38,6 @@ #include -#if LL_WINDOWS -// On windows, thread-local data is automatically exported. -#define LL_COMMON_API_TLS __declspec(thread) -#define CWD_TLS __declspec(thread) -#else -#define LL_COMMON_API_TLS LL_COMMON_API __thread -#define CWD_TLS __thread -#endif - namespace debug { namespace libcwd { diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp index dfb4a2660..7d179e8e3 100644 --- a/indra/llcommon/aithreadid.cpp +++ b/indra/llcommon/aithreadid.cpp @@ -34,8 +34,9 @@ AIThreadID const AIThreadID::sNone(AIThreadID::none); apr_os_thread_t AIThreadID::sMainThreadID; +apr_os_thread_t const undefinedID = (apr_os_thread_t)-1; #ifndef LL_DARWIN -apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread = AIThreadID::undefinedID; +apr_os_thread_t CWD_TLS AIThreadID::lCurrentThread; #endif void AIThreadID::set_main_thread_id(void) diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h index c565b5913..9f7893c66 100644 --- a/indra/llcommon/aithreadid.h +++ b/indra/llcommon/aithreadid.h @@ -33,28 +33,22 @@ #include // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal(). #include // std::ostream. -#include "llpreprocessor.h" // LL_COMMON_API +#include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS #include "llerror.h" -#if LL_WINDOWS -#define ll_thread_local __declspec(thread) -#else -#define ll_thread_local __thread -#endif - // Lightweight wrapper around apr_os_thread_t. // This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety. -class LL_COMMON_API AIThreadID +class AIThreadID { private: apr_os_thread_t mID; - static apr_os_thread_t sMainThreadID; - static apr_os_thread_t const undefinedID = (apr_os_thread_t)-1; + static LL_COMMON_API apr_os_thread_t sMainThreadID; + static LL_COMMON_API apr_os_thread_t const undefinedID; #ifndef LL_DARWIN - static ll_thread_local apr_os_thread_t lCurrentThread; + static LL_COMMON_API_TLS apr_os_thread_t lCurrentThread; #endif public: - static AIThreadID const sNone; + static LL_COMMON_API AIThreadID const sNone; enum undefined_thread_t { none }; enum dout_print_t { DoutPrint }; diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 009871908..be0ec94a3 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -195,4 +195,16 @@ # define LL_COMMON_API #endif // LL_COMMON_LINK_SHARED +// Darwin does not support thread-local data. +#ifndef LL_DARWIN +#if LL_WINDOWS +// On windows, thread-local data is automatically exported. +#define LL_COMMON_API_TLS __declspec(thread) +#define CWD_TLS __declspec(thread) +#else // Linux +#define LL_COMMON_API_TLS LL_COMMON_API __thread +#define CWD_TLS __thread +#endif +#endif + #endif // not LL_LINDEN_PREPROCESSOR_H From 91df8446b0ec20700787af739384075b8c89fb46 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 10 Aug 2012 05:37:41 +0200 Subject: [PATCH 086/123] Oops bug fix for last commit. --- indra/llcommon/aithreadid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp index 7d179e8e3..4eea7d37d 100644 --- a/indra/llcommon/aithreadid.cpp +++ b/indra/llcommon/aithreadid.cpp @@ -34,7 +34,7 @@ AIThreadID const AIThreadID::sNone(AIThreadID::none); apr_os_thread_t AIThreadID::sMainThreadID; -apr_os_thread_t const undefinedID = (apr_os_thread_t)-1; +apr_os_thread_t const AIThreadID::undefinedID = (apr_os_thread_t)-1; #ifndef LL_DARWIN apr_os_thread_t CWD_TLS AIThreadID::lCurrentThread; #endif From eef51e0bd033188c3d3288be0f769d8c39c2dc25 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 9 Aug 2012 22:50:05 -0500 Subject: [PATCH 087/123] Pulled llpacketring.h and llhttpclient.h out of the precompiled header. --- indra/llmessage/llcircuit.cpp | 3 ++- indra/llmessage/llpacketring.h | 2 +- indra/llmessage/message.cpp | 19 ++++++++++++------- indra/llmessage/message.h | 14 ++++++++++---- indra/newview/llstartup.cpp | 10 +++++----- indra/newview/llviewermenu.cpp | 2 +- indra/newview/llviewerprecompiledheaders.h | 2 +- indra/newview/llworld.cpp | 4 ++-- indra/newview/scriptcounter.cpp | 12 ++++++------ 9 files changed, 40 insertions(+), 28 deletions(-) diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index 4b41abd45..56249db41 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -56,6 +56,7 @@ #include "llstl.h" #include "lltransfermanager.h" #include "llmodularmath.h" +#include "llpacketring.h" const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked. const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked. @@ -346,7 +347,7 @@ S32 LLCircuitData::resendUnackedPackets(const F64 now) packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend - gMessageSystem->mPacketRing.sendPacket(packetp->mSocket, + gMessageSystem->mPacketRing->sendPacket(packetp->mSocket, (char *)packetp->mBuffer, packetp->mBufferLength, packetp->mHost); diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h index b214271e7..63d272308 100644 --- a/indra/llmessage/llpacketring.h +++ b/indra/llmessage/llpacketring.h @@ -32,7 +32,7 @@ #include "llhost.h" #include "llpacketbuffer.h" -#include "llproxy.h" +//#include "llproxy.h" #include "llthrottle.h" #include "net.h" diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 7d21e35f9..7abfea8ef 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -81,6 +81,7 @@ #include "v4math.h" #include "lltransfertargetvfile.h" #include "llmemtype.h" +#include "llpacketring.h" // Constants //const char* MESSAGE_LOG_FILENAME = "message.log"; @@ -245,7 +246,8 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, bool failure_is_fatal, const F32 circuit_heartbeat_interval, const F32 circuit_timeout) : mCircuitInfo(circuit_heartbeat_interval, circuit_timeout), - mLastMessageFromTrustedMessageService(false) + mLastMessageFromTrustedMessageService(false), + mPacketRing(new LLPacketRing) { init(); @@ -383,6 +385,9 @@ LLMessageSystem::~LLMessageSystem() delete mPollInfop; mPollInfop = NULL; + delete mPacketRing; + mPacketRing = NULL; + mIncomingCompressedSize = 0; mCurrentRecvPacketID = 0; } @@ -548,13 +553,13 @@ BOOL LLMessageSystem::checkMessages( S64 frame_count ) U8* buffer = mTrueReceiveBuffer; - mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer); + mTrueReceiveSize = mPacketRing->receivePacket(mSocket, (char *)mTrueReceiveBuffer); // If you want to dump all received packets into SecondLife.log, uncomment this //dumpPacketToLog(); receive_size = mTrueReceiveSize; - mLastSender = mPacketRing.getLastSender(); - mLastReceivingIF = mPacketRing.getLastReceivingInterface(); + mLastSender = mPacketRing->getLastSender(); + mLastReceivingIF = mPacketRing->getLastReceivingInterface(); if (receive_size < (S32) LL_MINIMUM_VALID_PACKET_SIZE) { @@ -1129,7 +1134,7 @@ S32 LLMessageSystem::flushReliable(const LLHost &host) return send_bytes; } -LLHTTPClient::ResponderPtr LLMessageSystem::createResponder(const std::string& name) +LLFnPtrResponder* LLMessageSystem::createResponder(const std::string& name) { if(mSendReliable) { @@ -1328,7 +1333,7 @@ S32 LLMessageSystem::sendMessage(const LLHost &host) } BOOL success; - success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host); + success = mPacketRing->sendPacket(mSocket, (char *)buf_ptr, buffer_length, host); if (!success) { @@ -3361,7 +3366,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ void LLMessageSystem::dumpPacketToLog() { - LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing.getLastSender() << llendl; + LL_WARNS("Messaging") << "Packet Dump from:" << mPacketRing->getLastSender() << llendl; LL_WARNS("Messaging") << "Packet Size:" << mTrueReceiveSize << llendl; char line_buffer[256]; /* Flawfinder: ignore */ S32 i; diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 1589ea29c..dee687d47 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -48,9 +48,9 @@ #include "string_table.h" #include "llcircuit.h" #include "lltimer.h" -#include "llpacketring.h" +//#include "llpacketring.h" #include "llhost.h" -#include "llhttpclient.h" +//#include "llhttpclient.h" #include "llhttpnode.h" #include "llpacketack.h" #include "llsingleton.h" @@ -61,6 +61,12 @@ #include "llstoredmessage.h" +class LLPacketRing; +namespace +{ + class LLFnPtrResponder; +} + const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -213,7 +219,7 @@ class LLMessageSystem : public LLMessageSenderInterface LLHost mUntrustedInterface; public: - LLPacketRing mPacketRing; + LLPacketRing* mPacketRing; LLReliablePacketParams mReliablePacketParams; // Set this flag to TRUE when you want *very* verbose logs. @@ -494,7 +500,7 @@ public: void (*callback)(void **,S32), void ** callback_data); - LLHTTPClient::ResponderPtr createResponder(const std::string& name); + LLFnPtrResponder* createResponder(const std::string& name); S32 sendMessage(const LLHost &host); S32 sendMessage(const U32 circuit); private: diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 1400da474..fcd46b82e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -627,21 +627,21 @@ bool idle_startup() F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage"); - msg->mPacketRing.setDropPercentage(dropPercent); + msg->mPacketRing->setDropPercentage(dropPercent); F32 inBandwidth = gSavedSettings.getF32("InBandwidth"); F32 outBandwidth = gSavedSettings.getF32("OutBandwidth"); if (inBandwidth != 0.f) { LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL; - msg->mPacketRing.setUseInThrottle(TRUE); - msg->mPacketRing.setInBandwidth(inBandwidth); + msg->mPacketRing->setUseInThrottle(TRUE); + msg->mPacketRing->setInBandwidth(inBandwidth); } if (outBandwidth != 0.f) { LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL; - msg->mPacketRing.setUseOutThrottle(TRUE); - msg->mPacketRing.setOutBandwidth(outBandwidth); + msg->mPacketRing->setUseOutThrottle(TRUE); + msg->mPacketRing->setOutBandwidth(outBandwidth); } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 669c57d43..5d6134412 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -4035,7 +4035,7 @@ void print_packets_lost(void*) void drop_packet(void*) { - gMessageSystem->mPacketRing.dropPackets(1); + gMessageSystem->mPacketRing->dropPackets(1); } diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index 031e5fa6a..e838eaadd 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -169,7 +169,7 @@ #include "llnamevalue.h" #include "llpacketack.h" #include "llpacketbuffer.h" -#include "llpacketring.h" +//#include "llpacketring.h" #include "llpartdata.h" //#include "llqueryflags.h" //#include "llregionflags.h" diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 6f92bedf9..ef77a099d 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -779,8 +779,8 @@ void LLWorld::updateNetStats() S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut; S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost; - S32 actual_in_bits = gMessageSystem->mPacketRing.getAndResetActualInBits(); - S32 actual_out_bits = gMessageSystem->mPacketRing.getAndResetActualOutBits(); + S32 actual_in_bits = gMessageSystem->mPacketRing->getAndResetActualInBits(); + S32 actual_out_bits = gMessageSystem->mPacketRing->getAndResetActualOutBits(); LLViewerStats::getInstance()->mActualInKBitStat.addValue(actual_in_bits/1024.f); LLViewerStats::getInstance()->mActualOutKBitStat.addValue(actual_out_bits/1024.f); LLViewerStats::getInstance()->mKBitStat.addValue(bits/1024.f); diff --git a/indra/newview/scriptcounter.cpp b/indra/newview/scriptcounter.cpp index 37acc1e37..f58d26b34 100644 --- a/indra/newview/scriptcounter.cpp +++ b/indra/newview/scriptcounter.cpp @@ -201,8 +201,8 @@ void ScriptCounter::serializeSelection(bool delScript) F32 throttle = gSavedSettings.getF32("OutBandwidth"); if((throttle == 0.f) || (throttle > 128000.f)) { - gMessageSystem->mPacketRing.setOutBandwidth(128000); - gMessageSystem->mPacketRing.setUseOutThrottle(TRUE); + gMessageSystem->mPacketRing->setOutBandwidth(128000); + gMessageSystem->mPacketRing->setUseOutThrottle(TRUE); } showResult(llformat("Counting scripts, please wait...")); if((objectCount == 1) && !(foo->isAvatar())) @@ -340,13 +340,13 @@ void ScriptCounter::completechk() F32 throttle = gSavedSettings.getF32("OutBandwidth"); if(throttle != 0.f) { - gMessageSystem->mPacketRing.setOutBandwidth(throttle); - gMessageSystem->mPacketRing.setUseOutThrottle(TRUE); + gMessageSystem->mPacketRing->setOutBandwidth(throttle); + gMessageSystem->mPacketRing->setUseOutThrottle(TRUE); } else { - gMessageSystem->mPacketRing.setOutBandwidth(0.0); - gMessageSystem->mPacketRing.setUseOutThrottle(FALSE); + gMessageSystem->mPacketRing->setOutBandwidth(0.0); + gMessageSystem->mPacketRing->setUseOutThrottle(FALSE); } llinfos << "Sending readout to chat..." << llendl; showResult(user_msg); From 94ebcdeb6b2a318d826c548eeff13dcb52bd184d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 10 Aug 2012 07:23:29 +0200 Subject: [PATCH 088/123] Add eight missing headers... How did you ever get this to compile if those headers were removed from the precompiled header? --- indra/newview/llfloaterurlentry.cpp | 1 + indra/newview/llstartup.cpp | 1 + indra/newview/lltexturestatsuploader.cpp | 1 + indra/newview/lluserauth.cpp | 4 ++++ indra/newview/llviewermedia.cpp | 1 + indra/newview/llviewermenu.cpp | 1 + indra/newview/llworld.cpp | 1 + indra/newview/scriptcounter.cpp | 1 + 8 files changed, 11 insertions(+) diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 35dc9ddda..c56e49206 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -43,6 +43,7 @@ #include "lluictrlfactory.h" #include "llwindow.h" #include "llviewerwindow.h" +#include "llhttpclient.h" static LLFloaterURLEntry* sInstance = NULL; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index fcd46b82e..328ccf2dc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -219,6 +219,7 @@ #include "llfloaterblacklist.h" #include "scriptcounter.h" #include "shfloatermediaticker.h" +#include "llpacketring.h" // #include "llavatarnamecache.h" diff --git a/indra/newview/lltexturestatsuploader.cpp b/indra/newview/lltexturestatsuploader.cpp index e0358e1fc..7f14a4254 100644 --- a/indra/newview/lltexturestatsuploader.cpp +++ b/indra/newview/lltexturestatsuploader.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "lltexturestatsuploader.h" +#include "llhttpclient.h" LLTextureStatsUploader::LLTextureStatsUploader() { diff --git a/indra/newview/lluserauth.cpp b/indra/newview/lluserauth.cpp index 29930a96e..a10c66fca 100644 --- a/indra/newview/lluserauth.cpp +++ b/indra/newview/lluserauth.cpp @@ -49,6 +49,10 @@ // NOTE: MUST include these after otherincludes since queue gets redefined!?!! #include +#include +#ifdef DEBUG_CURLIO +#include "debug_libcurl.h" +#endif // Don't define PLATFORM_STRING for unknown platforms - they need // to get added to the login cgi script, so we want this to cause an diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 56f0403e9..7d83e718d 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -48,6 +48,7 @@ #include "llnotificationsutil.h" #include "lluuid.h" #include "llkeyboard.h" +#include "llhttpclient.h" // Merov: Temporary definitions while porting the new viewer media code to Snowglobe const int LEFT_BUTTON = 0; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 5d6134412..a30508522 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -250,6 +250,7 @@ #include "llfloatervfs.h" #include "llfloatervfsexplorer.h" #include "shfloatermediaticker.h" +#include "llpacketring.h" // #include "scriptcounter.h" diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index ef77a099d..a265b3613 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -61,6 +61,7 @@ #include "message.h" #include "pipeline.h" #include "llappviewer.h" // for do_disconnect() +#include "llpacketring.h" #include #include diff --git a/indra/newview/scriptcounter.cpp b/indra/newview/scriptcounter.cpp index f58d26b34..3da258c28 100644 --- a/indra/newview/scriptcounter.cpp +++ b/indra/newview/scriptcounter.cpp @@ -43,6 +43,7 @@ #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" +#include "llpacketring.h" #include ScriptCounter* ScriptCounter::sInstance; From 70717cb6c897f6f0babfa3596f4d7a59efa88a8d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 10 Aug 2012 14:49:34 +0200 Subject: [PATCH 089/123] Compile bug fix for windows. --- indra/llmessage/aicurlprivate.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index ead30e82e..a39120faa 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -98,11 +98,10 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven DECLARE_SETOPT(curl_chunk_end_callback); DECLARE_SETOPT(curl_fnmatch_callback); #endif -#if __LP64__ // sizeof(long) > sizeof(S32), see http://en.cppreference.com/w/cpp/language/types - // Automatically cast small int types to a long if they differ in size. + // Automatically cast int types to a long. Note that U32/S32 are int and + // that you can overload int and long even if they have the same size. CURLcode setopt(CURLoption option, U32 parameter) { return setopt(option, (long)parameter); } CURLcode setopt(CURLoption option, S32 parameter) { return setopt(option, (long)parameter); } -#endif // Clone a libcurl session handle using all the options previously set. //CurlEasyHandle(CurlEasyHandle const& orig); From 3469d65f9514de3d4779031cb9e1839e3a904887 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 11 Aug 2012 03:23:31 +0200 Subject: [PATCH 090/123] Windows compile and linker fixes. * Do not include aithreadid.h from debug.h, because the latter is included everywhere (from linden_common.h) and aithreadid.h is heavy (includes among others windows.h). * On windows, thread local members cannot be exported. --- indra/cwdebug/debug.cc | 26 ++++++++++++++++++++++- indra/cwdebug/debug.h | 15 ++++++------- indra/llcommon/aithreadid.cpp | 37 +++++++++++++++++++-------------- indra/llcommon/aithreadid.h | 17 ++++++++------- indra/llcommon/llaprpool.cpp | 6 +++--- indra/llcommon/llpreprocessor.h | 7 ++----- indra/llcommon/llrefcount.cpp | 4 ++-- indra/llcommon/llthread.cpp | 14 ++++++------- 8 files changed, 78 insertions(+), 48 deletions(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 18b51aad0..92ffc0d13 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -416,12 +416,23 @@ void cwdebug_backtrace(int n) #elif defined(DEBUG_CURLIO) #include "debug.h" +#include "aithreadid.h" namespace debug { libcwd_do_type const libcw_do; -CWD_TLS int Indent::S_indentation; +ll_thread_local int Indent::S_indentation; + +Indent::Indent(int indent) : M_indent(indent) +{ + S_indentation += M_indent; +} + +Indent::~Indent() +{ + S_indentation -= M_indent; +} std::ostream& operator<<(std::ostream& os, Indent::print_nt) { @@ -430,6 +441,19 @@ std::ostream& operator<<(std::ostream& os, Indent::print_nt) return os; } +std::ostream& operator<<(std::ostream& os, print_thread_id_t) +{ + if (!AIThreadID::in_main_thread_inline()) + { +#ifdef LL_DARWIN + os << std::hex << (size_t)apr_os_thread_current() << std::dec << ' '; +#else + os << std::hex << (size_t)AIThreadID::getCurrentThread_inline() << std::dec << ' '; +#endif + } + return os; +} + std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s) { static char const c2s_tab[7] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' }; diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 9e832a44c..c8f15cfb9 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -29,14 +29,13 @@ #ifdef DEBUG_CURLIO -#include "aithreadid.h" - // If CWDEBUG is not defined, but DEBUG_CURLIO is, then replace // some of the cwd macro's with something that generates viewer // specific debug output. Note that this generates a LOT of // output and should not normally be defined. #include +#include "llpreprocessor.h" namespace debug { namespace libcwd { @@ -49,6 +48,7 @@ struct buf2str { } // namespace libcwd +enum print_thread_id_t { print_thread_id }; inline void init() { } struct libcwd_do_type { void on() const { } @@ -56,14 +56,15 @@ struct libcwd_do_type { extern LL_COMMON_API libcwd_do_type const libcw_do; struct Indent { int M_indent; - static LL_COMMON_API_TLS int S_indentation; + static ll_thread_local int S_indentation; enum LL_COMMON_API print_nt { print }; - LL_COMMON_API Indent(int indent) : M_indent(indent) { S_indentation += M_indent; } - LL_COMMON_API ~Indent() { S_indentation -= M_indent; } + LL_COMMON_API Indent(int indent); + LL_COMMON_API ~Indent(); }; extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s); extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, Indent::print_nt); +extern LL_COMMON_API std::ostream& operator<<(std::ostream& os, print_thread_id_t); namespace dc { @@ -87,13 +88,13 @@ extern LL_COMMON_API fake_channel const notice; } // namespace debug #define Debug(x) do { using namespace debug; x; } while(0) -#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) #define DoutEntering(a, b) \ int __slviewer_debug_indentation = 2; \ { \ using namespace debug; \ if ((a).mOn) \ - llinfos_nf << AIThreadID::DoutPrint << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ + llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ else \ __slviewer_debug_indentation = 0; \ } \ diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp index 4eea7d37d..64a8dd21a 100644 --- a/indra/llcommon/aithreadid.cpp +++ b/indra/llcommon/aithreadid.cpp @@ -27,7 +27,6 @@ * - Initial version, written by Aleric Inglewood @ SL */ -#include "sys.h" #include #include #include "aithreadid.h" @@ -36,7 +35,7 @@ AIThreadID const AIThreadID::sNone(AIThreadID::none); apr_os_thread_t AIThreadID::sMainThreadID; apr_os_thread_t const AIThreadID::undefinedID = (apr_os_thread_t)-1; #ifndef LL_DARWIN -apr_os_thread_t CWD_TLS AIThreadID::lCurrentThread; +apr_os_thread_t ll_thread_local AIThreadID::lCurrentThread; #endif void AIThreadID::set_main_thread_id(void) @@ -51,21 +50,27 @@ void AIThreadID::set_current_thread_id(void) #endif } +void AIThreadID::reset(void) +{ + mID = lCurrentThread; +} + +bool AIThreadID::equals_current_thread(void) const +{ + return apr_os_thread_equal(mID, lCurrentThread); +} + +bool AIThreadID::in_main_thread(void) +{ + return apr_os_thread_equal(lCurrentThread, sMainThreadID); +} + +apr_os_thread_t AIThreadID::getCurrentThread(void) +{ + return lCurrentThread; +} + std::ostream& operator<<(std::ostream& os, AIThreadID const& id) { return os << id.mID; } - -std::ostream& operator<<(std::ostream& os, AIThreadID::dout_print_t) -{ - if (!AIThreadID::in_main_thread()) - { -#ifdef LL_DARWIN - os << std::hex << (size_t)apr_os_thread_current() << std::dec << ' '; -#else - os << std::hex << (size_t)AIThreadID::lCurrentThread << std::dec << ' '; -#endif - } - return os; -} - diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h index 9f7893c66..2bb3f1dac 100644 --- a/indra/llcommon/aithreadid.h +++ b/indra/llcommon/aithreadid.h @@ -34,7 +34,6 @@ #include // apr_os_thread_t, apr_os_thread_current(), apr_os_thread_equal(). #include // std::ostream. #include "llpreprocessor.h" // LL_COMMON_API, LL_COMMON_API_TLS -#include "llerror.h" // Lightweight wrapper around apr_os_thread_t. // This class introduces no extra assembly code after optimization; it's only intend is to provide type-safety. @@ -45,12 +44,11 @@ private: static LL_COMMON_API apr_os_thread_t sMainThreadID; static LL_COMMON_API apr_os_thread_t const undefinedID; #ifndef LL_DARWIN - static LL_COMMON_API_TLS apr_os_thread_t lCurrentThread; + static ll_thread_local apr_os_thread_t lCurrentThread; #endif public: static LL_COMMON_API AIThreadID const sNone; enum undefined_thread_t { none }; - enum dout_print_t { DoutPrint }; public: AIThreadID(void) : mID(apr_os_thread_current()) { } @@ -62,7 +60,6 @@ public: friend LL_COMMON_API bool operator==(AIThreadID const& id1, AIThreadID const& id2) { return apr_os_thread_equal(id1.mID, id2.mID); } friend LL_COMMON_API bool operator!=(AIThreadID const& id1, AIThreadID const& id2) { return !apr_os_thread_equal(id1.mID, id2.mID); } friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id); - friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, dout_print_t); static void set_main_thread_id(void); // Called once to set sMainThreadID. static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread. #ifdef LL_DARWIN @@ -70,9 +67,15 @@ public: bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } #else - void reset(void) { mID = lCurrentThread; } - bool equals_current_thread(void) const { return apr_os_thread_equal(mID, lCurrentThread); } - static bool in_main_thread(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); } + LL_COMMON_API void reset(void); + LL_COMMON_API bool equals_current_thread(void) const; + LL_COMMON_API static bool in_main_thread(void); + LL_COMMON_API static apr_os_thread_t getCurrentThread(void); + // The *_inline variants cannot be exported because they access a thread-local member. + void reset_inline(void) { mID = lCurrentThread; } + bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, lCurrentThread); } + static bool in_main_thread_inline(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); } + static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; } #endif }; diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp index de5761157..3dffa8300 100644 --- a/indra/llcommon/llaprpool.cpp +++ b/indra/llcommon/llaprpool.cpp @@ -60,10 +60,10 @@ void LLAPRPool::create(LLAPRPool& parent) // // In other words, it's safe for any thread to create a (sub)pool, independent of who // owns the parent pool. - mOwner.reset(); + mOwner.reset_inline(); #else mOwner = mParent->mOwner; - llassert(mOwner.equals_current_thread()); + llassert(mOwner.equals_current_thread_inline()); #endif apr_status_t const apr_pool_create_status = apr_pool_create(&mPool, mParent->mPool); llassert_always(apr_pool_create_status == APR_SUCCESS); @@ -83,7 +83,7 @@ void LLAPRPool::destroy(void) // of course. Otherwise, if we are a subpool, only the thread that owns // the parent may destruct us, since that is the pool that is still alive, // possibly being used by others and being altered here. - llassert(!mParent || mParent->mOwner.equals_current_thread()); + llassert(!mParent || mParent->mOwner.equals_current_thread_inline()); #endif apr_pool_t* pool = mPool; mPool = NULL; // Mark that we are BEING destructed. diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index be0ec94a3..a1f598d4a 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -198,12 +198,9 @@ // Darwin does not support thread-local data. #ifndef LL_DARWIN #if LL_WINDOWS -// On windows, thread-local data is automatically exported. -#define LL_COMMON_API_TLS __declspec(thread) -#define CWD_TLS __declspec(thread) +#define ll_thread_local __declspec(thread) #else // Linux -#define LL_COMMON_API_TLS LL_COMMON_API __thread -#define CWD_TLS __thread +#define ll_thread_local __thread #endif #endif diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp index d92735aac..0956e1628 100644 --- a/indra/llcommon/llrefcount.cpp +++ b/indra/llcommon/llrefcount.cpp @@ -75,7 +75,7 @@ void LLRefCount::ref() const } mMutex.lock() ; - mLockedThreadID.reset(); + mLockedThreadID.reset_inline(); mRef++; @@ -96,7 +96,7 @@ S32 LLRefCount::unref() const } mMutex.lock() ; - mLockedThreadID.reset(); + mLockedThreadID.reset_inline(); llassert(mRef >= 1); if (0 == --mRef) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index c8d0be2f4..5c1a29f24 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -72,7 +72,7 @@ LLAtomicS32 LLThread::sRunning = 0; LL_COMMON_API void assert_main_thread() { - if (!AIThreadID::in_main_thread()) + if (!AIThreadID::in_main_thread_inline()) { llerrs << "Illegal execution outside main thread." << llendl; } @@ -407,12 +407,12 @@ LLMutexBase::LLMutexBase() : bool LLMutexBase::isSelfLocked() const { - return mLockingThread.equals_current_thread(); + return mLockingThread.equals_current_thread_inline(); } void LLMutexBase::lock() { - if (mLockingThread.equals_current_thread()) + if (mLockingThread.equals_current_thread_inline()) { //redundant lock mCount++; return; @@ -420,12 +420,12 @@ void LLMutexBase::lock() apr_thread_mutex_lock(mAPRMutexp); - mLockingThread.reset(); + mLockingThread.reset_inline(); } bool LLMutexBase::tryLock() { - if (mLockingThread.equals_current_thread()) + if (mLockingThread.equals_current_thread_inline()) { //redundant lock mCount++; return true; @@ -433,7 +433,7 @@ bool LLMutexBase::tryLock() bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); if (success) { - mLockingThread.reset(); + mLockingThread.reset_inline(); } return success; } @@ -441,7 +441,7 @@ bool LLMutexBase::tryLock() // non-blocking, but does do a lock/unlock so not free bool LLMutexBase::isLocked() const { - if (mLockingThread.equals_current_thread()) + if (mLockingThread.equals_current_thread_inline()) return false; // A call to lock() won't block. if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp))) return true; From 8e1bbfe938e79ff4c37fcf60ca9493a56da08218 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 11 Aug 2012 03:29:46 +0200 Subject: [PATCH 091/123] Amend last commit --- indra/cwdebug/debug.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 92ffc0d13..b202a8ac6 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -441,6 +441,7 @@ std::ostream& operator<<(std::ostream& os, Indent::print_nt) return os; } +#ifdef DEBUG_CURLIO std::ostream& operator<<(std::ostream& os, print_thread_id_t) { if (!AIThreadID::in_main_thread_inline()) @@ -453,6 +454,7 @@ std::ostream& operator<<(std::ostream& os, print_thread_id_t) } return os; } +#endif std::ostream& operator<<(std::ostream& os, libcwd::buf2str const& b2s) { From 164799e4c48f77c4266ae3b464fbc08fca924a10 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 12 Aug 2012 03:33:33 +0200 Subject: [PATCH 092/123] Curl debug output improvements and minor fixes. --- indra/cwdebug/debug.cc | 3 +- indra/cwdebug/debug.h | 16 ++++-- indra/llmessage/aicurl.cpp | 10 +--- indra/llmessage/debug_libcurl.cpp | 90 ++++++++++++++++++++++++++++--- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index b202a8ac6..cad6da46d 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -421,7 +421,8 @@ void cwdebug_backtrace(int n) namespace debug { -libcwd_do_type const libcw_do; +namespace libcwd { libcwd_do_type const libcw_do; } + ll_thread_local int Indent::S_indentation; Indent::Indent(int indent) : M_indent(indent) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index c8f15cfb9..862ea263c 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -46,14 +46,15 @@ struct buf2str { int mSize; }; -} // namespace libcwd - -enum print_thread_id_t { print_thread_id }; -inline void init() { } struct libcwd_do_type { void on() const { } }; extern LL_COMMON_API libcwd_do_type const libcw_do; + +} // namespace libcwd + +enum print_thread_id_t { print_thread_id }; +inline void init() { } struct Indent { int M_indent; static ll_thread_local int S_indentation; @@ -87,8 +88,13 @@ extern LL_COMMON_API fake_channel const notice; } // namespace dc } // namespace debug +#define LIBCWD_DEBUG_CHANNELS debug +#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print; +#define LibcwDoutStream llcont +#define LibcwDoutScopeEnd llcont << llendl; } while(0) + #define Debug(x) do { using namespace debug; x; } while(0) -#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) #define DoutEntering(a, b) \ int __slviewer_debug_indentation = 2; \ { \ diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index d22408491..edbcfec83 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -958,13 +958,6 @@ void CurlEasyRequest::addHeader(char const* header) #if defined(CWDEBUG) || defined(DEBUG_CURLIO) -#ifndef CWDEBUG -#define LIBCWD_DEBUGCHANNELS 0 -#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; llinfos_nf << dc::curlio.mLabel << ": "; -#define LibcwDoutStream llcont -#define LibcwDoutScopeEnd llcont << llendl; } while(0) -#endif - static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr) { #ifdef CWDEBUG @@ -977,14 +970,15 @@ static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, libcw_do.marker().assign(marker.str().data(), marker.str().size()); if (!debug::channels::dc::curlio.is_on()) debug::channels::dc::curlio.on(); + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) #else if (infotype == CURLINFO_TEXT) { while (size > 0 && (buf[size - 1] == '\r' || buf[size - 1] == '\n')) --size; } + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curlio) #endif - LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) switch (infotype) { case CURLINFO_TEXT: diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index c981ef38f..3f85dcc2d 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -11,6 +11,9 @@ #include "debug_libcurl.h" #include "debug.h" #include "llerror.h" +#ifdef CWDEBUG +#include +#endif #define CURL_VERSION(major, minor, patch) \ (LIBCURL_VERSION_MAJOR > major || \ @@ -277,6 +280,7 @@ std::ostream& operator<<(std::ostream& os, CURLoption option) CASEPRINT(CURLOPT_PROGRESSDATA); CASEPRINT(CURLOPT_AUTOREFERER); CASEPRINT(CURLOPT_PROXYPORT); + CASEPRINT(CURLOPT_POSTFIELDSIZE); CASEPRINT(CURLOPT_HTTPPROXYTUNNEL); CASEPRINT(CURLOPT_INTERFACE); CASEPRINT(CURLOPT_KRBLEVEL); @@ -552,25 +556,26 @@ CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...) CURLcode ret; va_list ap; union param_type { + void* some_ptr; long* long_ptr; - char* char_ptr; - curl_slist* curl_slist_ptr; + char** char_ptr; + curl_slist** curl_slist_ptr; double* double_ptr; } param; va_start(ap, info); - param.long_ptr = va_arg(ap, long*); + param.some_ptr = va_arg(ap, void*); va_end(ap); - ret = curl_easy_getinfo(curl, info, param.long_ptr); + ret = curl_easy_getinfo(curl, info, param.some_ptr); if (info == CURLINFO_PRIVATE) { - Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.char_ptr << std::dec << ") = " << ret); + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", 0x" << std::hex << (size_t)param.some_ptr << std::dec << ") = " << ret); } else { switch((info & CURLINFO_TYPEMASK)) { case CURLINFO_STRING: - Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char*){ \"" << (ret == CURLE_OK ? param.char_ptr : " ") << "\" }) = " << ret); + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (char**){ \"" << (ret == CURLE_OK ? *param.char_ptr : " ") << "\" }) = " << ret); break; case CURLINFO_LONG: Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (long*){ " << (ret == CURLE_OK ? *param.long_ptr : 0L) << "L }) = " << ret); @@ -579,7 +584,7 @@ CURLcode debug_curl_easy_getinfo(CURL* curl, CURLINFO info, ...) Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (double*){" << (ret == CURLE_OK ? *param.double_ptr : 0.) << "}) = " << ret); break; case CURLINFO_SLIST: - Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist*){ " << (ret == CURLE_OK ? *param.curl_slist_ptr : unchanged_slist) << " }) = " << ret); + Dout(dc::curl, "curl_easy_getinfo(" << curl << ", " << info << ", (curl_slist**){ " << (ret == CURLE_OK ? **param.curl_slist_ptr : unchanged_slist) << " }) = " << ret); break; } } @@ -644,24 +649,93 @@ CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...) std::exit(EXIT_FAILURE); } va_end(ap); + static long postfieldsize; // Cache. Assumes only one thread sets CURLOPT_POSTFIELDSIZE. switch (param_type) { case CURLOPTTYPE_LONG: + { ret = curl_easy_setopt(handle, option, param.along); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", " << param.along << "L) = " << ret); + if (option == CURLOPT_POSTFIELDSIZE) + { + postfieldsize = param.along; + } break; + } case CURLOPTTYPE_OBJECTPOINT: + { ret = curl_easy_setopt(handle, option, param.ptr); - Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (object*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curl) + LibcwDoutStream << "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", "; + // For a subset of all options that take a char*, print the string passed. + if (option == CURLOPT_PROXY || // Set HTTP proxy to use. The parameter should be a char* to a zero terminated string holding the host name or dotted IP address. + option == CURLOPT_PROXYUSERPWD || // Pass a char* as parameter, which should be [user name]:[password] to use for the connection to the HTTP proxy. + option == CURLOPT_CAINFO || // Pass a char * to a zero terminated string naming a file holding one or more certificates to verify the peer with. + option == CURLOPT_URL || // Pass in a pointer to the actual URL to deal with. The parameter should be a char * to a zero terminated string [...] + option == CURLOPT_COOKIEFILE || // Pass a pointer to a zero terminated string as parameter. It should contain the name of your file holding cookie data to read. + option == CURLOPT_CUSTOMREQUEST || // Pass a pointer to a zero terminated string as parameter. It can be used to specify the request instead of GET or HEAD when performing HTTP based requests + option == CURLOPT_ENCODING || // Sets the contents of the Accept-Encoding: header sent in a HTTP request, and enables decoding of a response when a Content-Encoding: header is received. + option == CURLOPT_POSTFIELDS || + option == CURLOPT_COPYPOSTFIELDS) // Full data to post in a HTTP POST operation. + { + bool const is_postfield = option == CURLOPT_POSTFIELDS || option == CURLOPT_COPYPOSTFIELDS; + char* str = (char*)param.ptr; + long size; + LibcwDoutStream << "(char*)0x" << std::hex << (size_t)param.ptr << std::dec << ")"; + if (is_postfield) + { + size = postfieldsize < 32 ? postfieldsize : 32; // Only print first 32 characters (this was already written to debug output before). + } + else + { + size = strlen(str); + } + LibcwDoutStream << "["; + if (str) + { + LibcwDoutStream << libcwd::buf2str(str, size); + if (is_postfield && postfieldsize > 32) + LibcwDoutStream << "..."; + } + else + { + LibcwDoutStream << "NULL"; + } + LibcwDoutStream << "](" << (is_postfield ? postfieldsize : size) << " bytes))"; + } + else + { + LibcwDoutStream << "(object*)0x" << std::hex << (size_t)param.ptr << std::dec << ")"; + } + LibcwDoutStream << " = " << ret; + LibcwDoutScopeEnd; + if (option == CURLOPT_HTTPHEADER && param.ptr) + { + debug::Indent indent(2); + Dout(dc::curl, "HTTP Headers:"); + struct curl_slist* list = (struct curl_slist*)param.ptr; + while (list) + { + Dout(dc::curl, '"' << list->data << '"'); + list = list->next; + } + } break; + } case CURLOPTTYPE_FUNCTIONPOINT: ret = curl_easy_setopt(handle, option, param.ptr); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (function*)0x" << std::hex << (size_t)param.ptr << std::dec << ") = " << ret); break; case CURLOPTTYPE_OFF_T: + { ret = curl_easy_setopt(handle, option, param.offset); Dout(dc::curl, "curl_easy_setopt(" << (AICURL*)handle << ", " << option << ", (curl_off_t)" << param.offset << ") = " << ret); + if (option == CURLOPT_POSTFIELDSIZE_LARGE) + { + postfieldsize = (long)param.offset; + } break; + } default: break; } From 2fbe7ab6ce2c06975bc7e4cec19867bc90e67d61 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 12 Aug 2012 03:38:45 +0200 Subject: [PATCH 093/123] Only accept pointers to valid types for getinfo(). --- indra/llmessage/aicurl.cpp | 2 +- indra/llmessage/aicurlprivate.h | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index edbcfec83..daba6f923 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -657,7 +657,7 @@ void CurlEasyHandle::setErrorBuffer(void) } } -CURLcode CurlEasyHandle::getinfo(CURLINFO info, void* data) +CURLcode CurlEasyHandle::getinfo_priv(CURLINFO info, void* data) { setErrorBuffer(); return check_easy_code(curl_easy_getinfo(mEasyHandle, info, data)); diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index a39120faa..309992973 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -111,11 +111,21 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven char* unescape(char* url, int inlength , int* outlength); // Extract information from a curl handle. - CURLcode getinfo(CURLINFO info, void* data); -#if _WIN64 || __x86_64__ || __ppc64__ + private: + CURLcode getinfo_priv(CURLINFO info, void* data); + public: + // The rest are inlines to provide some type-safety. + CURLcode getinfo(CURLINFO info, char** data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, curl_slist** data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, double* data) { return getinfo_priv(info, data); } + CURLcode getinfo(CURLINFO info, long* data) { return getinfo_priv(info, data); } +#ifdef __LP64__ // sizeof(long) > sizeof(int) ? // Overload for integer types that are too small (libcurl demands a long). - CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo(info, &ldata); *data = static_cast(ldata); return res; } - CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo(info, &ldata); *data = static_cast(ldata); return res; } + CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } + CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } +#else + CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, static_cast(data)); } + CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, static_cast(data)); } #endif // Perform a file transfer (blocking). From 304fa05094932cae7ebc0a9de0fd8c3950e0ce59 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 12 Aug 2012 16:34:20 +0200 Subject: [PATCH 094/123] Suppress "Expect:" header for POST and PUT. No longer use CURLOPT_COPYPOSTFIELDS. For POST and PUT, libcurl adds by default a "Expect: 100-continue" header and wait for the server to reply with a 100 response. However, since the server never does that (LL comment in the code "I'm not sure what it means") it's up to libcurl to continue anyway after a while and that part is apparently bugged, causing people not to be able to login sometimes. But suppressing the header, libcurl doesn't wait for it and we worked around this bug. The commit introduces a much improved CurlEasyRequest::setPost method that enforces the above for POST, but that also no longer uses CURLOPT_COPYPOSTFIELDS (but CURLOPT_POSTFIELDS), no longer making a copy of the body of what we are going to send to the server. Instead it uses a new object, derived from AIPostField, to keep track of this data and to dispose of it once the transaction is complete (and no sooner). Copied from llcorehttp, we now also always set a "Connection: keep-alive" and "Keep-alive: 300" header for POST. This was already done for texture downloads (HttpOpRequest), but now we do it always :p. Might need to be changed in the future, but currently those headers are ignored by the server anyway. --- indra/llmessage/aicurl.cpp | 53 +++++++++++++++++++++++---- indra/llmessage/aicurl.h | 15 ++++++++ indra/llmessage/aicurlprivate.h | 10 ++++- indra/llmessage/llhttpclient.cpp | 21 +++-------- indra/llmessage/llurlrequest.cpp | 14 ++----- indra/newview/llcurlrequest.cpp | 4 +- indra/newview/llxmlrpctransaction.cpp | 27 ++++++-------- 7 files changed, 91 insertions(+), 53 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index daba6f923..ceb1000d5 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -701,6 +701,7 @@ CURLMcode CurlEasyHandle::remove_handle_from_multi(AICurlEasyRequest_wat& curl_e mActiveMultiHandle = NULL; CURLMcode res = check_multi_code(curl_multi_remove_handle(multi, mEasyHandle)); removed_from_multi_handle(curl_easy_request_w); + mPostField = NULL; return res; } @@ -796,11 +797,33 @@ void CurlEasyRequest::setoptString(CURLoption option, std::string const& value) setopt(option, value.c_str()); } -void CurlEasyRequest::setPost(char const* postdata, S32 size) +void CurlEasyRequest::setPost(AIPostFieldPtr const& postdata, S32 size) { - setopt(CURLOPT_POST, 1L); + llassert_always(postdata->data()); + + Dout(dc::curl, "POST size is " << size << " bytes: \"" << libcwd::buf2str(postdata->data(), size) << "\"."); + setPostField(postdata); // Make sure the data stays around until we don't need it anymore. + + setPost_raw(size, postdata->data()); +} + +void CurlEasyRequest::setPost_raw(S32 size, char const* data) +{ + if (!data) + { + // data == NULL when we're going to read the data using CURLOPT_READFUNCTION. + Dout(dc::curl, "POST size is " << size << " bytes."); + } + + // The server never replies with 100-continue, so suppress the "Expect: 100-continue" header that libcurl adds by default. + addHeader("Expect:"); + if (size > 0) + { + addHeader("Connection: keep-alive"); + addHeader("Keep-alive: 300"); + } setopt(CURLOPT_POSTFIELDSIZE, size); - setopt(CURLOPT_POSTFIELDS, postdata); + setopt(CURLOPT_POSTFIELDS, data); } ThreadSafeCurlEasyRequest* CurlEasyRequest::get_lockobj(void) @@ -1133,8 +1156,23 @@ void CurlEasyRequest::finalizeRequest(std::string const& url) { llassert(!mRequestFinalized); mResult = CURLE_FAILED_INIT; // General error code, the final code is written here in MultiHandle::check_run_count when msg is CURLMSG_DONE. - mRequestFinalized = true; lldebugs << url << llendl; +#ifdef SHOW_ASSERT + // Do a sanity check on the headers. + int content_type_count = 0; + for (curl_slist* list = mHeaders; list; list = list->next) + { + if (strncmp(list->data, "Content-Type:", 13) == 0) + { + ++content_type_count; + } + } + if (content_type_count > 1) + { + llwarns << content_type_count << " Content-Type: headers!" << llendl; + } +#endif + mRequestFinalized = true; setopt(CURLOPT_HTTPHEADER, mHeaders); setoptString(CURLOPT_URL, url); // The following line is a bit tricky: we store a pointer to the object without increasing its reference count. @@ -1270,7 +1308,8 @@ void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w { if (post) { - curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); + // Accept everything (send an Accept-Encoding header containing all encodings we support (zlib and gzip)). + curl_easy_request_w->setoptString(CURLOPT_ENCODING, ""); // CURLOPT_ACCEPT_ENCODING } mInput.reset(new LLBufferArray); @@ -1303,9 +1342,7 @@ void CurlResponderBuffer::prepRequest(AICurlEasyRequest_wat& curl_easy_request_w if (!post) { - curl_easy_request_w->addHeader("Connection: keep-alive"); - curl_easy_request_w->addHeader("Keep-alive: 300"); - // Add other headers. + // Add extra headers. for (std::vector::const_iterator iter = headers.begin(); iter != headers.end(); ++iter) { curl_easy_request_w->addHeader((*iter).c_str()); diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index 895650ec8..26491da67 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -247,6 +247,21 @@ struct AICurlEasyHandleEvents { virtual ~AICurlEasyHandleEvents() { } }; +// Pointer to data we're going to POST. +class AIPostField : public LLThreadSafeRefCount { + protected: + char const* mData; + + public: + AIPostField(char const* data) : mData(data) { } + char const* data(void) const { return mData; } +}; + +// The pointer to the data that we have to POST is passed around as AIPostFieldPtr, +// which causes it to automatically clean up when there are no pointers left +// pointing to it. +typedef LLPointer AIPostFieldPtr; + #include "aicurlprivate.h" // AICurlPrivate::CurlEasyRequestPtr, a boost::intrusive_ptr, is no more threadsafe than a diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 309992973..a475504b1 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -143,6 +143,7 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven CURL* mEasyHandle; CURLM* mActiveMultiHandle; char* mErrorBuffer; + AIPostFieldPtr mPostField; // This keeps the POSTFIELD data alive for as long as the easy handle exists. bool mQueuedForRemoval; // Set if the easy handle is (probably) added to the multi handle, but is queued for removal. #ifdef SHOW_ASSERT public: @@ -190,6 +191,9 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven // Return the underlying curl easy handle. CURL* getEasyHandle(void) const { return mEasyHandle; } + // Keep POSTFIELD data alive. + void setPostField(AIPostFieldPtr const& post_field_ptr) { mPostField = post_field_ptr; } + private: // Return, and possibly create, the curl (easy) error buffer used by the current thread. static char* getTLErrorBuffer(void); @@ -208,9 +212,13 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven // AICurlEasyRequest is deleted, then also the ThreadSafeCurlEasyRequest is deleted // and the CurlEasyRequest destructed. class CurlEasyRequest : public CurlEasyHandle { + private: + void setPost_raw(S32 size, char const* data); public: + void setPost(S32 size) { setPost_raw(size, NULL); } + void setPost(AIPostFieldPtr const& postdata, S32 size); + void setPost(char const* data, S32 size) { setPost(new AIPostField(data), size); } void setoptString(CURLoption option, std::string const& value); - void setPost(char const* postdata, S32 size); void addHeader(char const* str); private: diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 9c58bc466..5e201710f 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -443,7 +443,6 @@ static LLSD blocking_request( AICurlEasyRequest_wat curlEasyRequest_w(*easy_request); LLHTTPBuffer http_buffer; - std::string body_str; // * Set curl handle options curlEasyRequest_w->setopt(CURLOPT_TIMEOUT, (long)timeout); // seconds, see warning at top of function. @@ -463,6 +462,9 @@ static LLSD blocking_request( } } + // Needs to stay alive until after the call to perform(). + std::ostringstream ostr; + // * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) if (method == LLURLRequest::HTTP_GET) { @@ -470,24 +472,13 @@ static LLSD blocking_request( } else if (method == LLURLRequest::HTTP_POST) { - curlEasyRequest_w->setopt(CURLOPT_POST, 1); - //serialize to ostr then copy to str - need to because ostr ptr is unstable :( - std::ostringstream ostr; - LLSDSerialize::toXML(body, ostr); - body_str = ostr.str(); - curlEasyRequest_w->setopt(CURLOPT_POSTFIELDS, body_str.c_str()); //copied from PHP libs, correct? curlEasyRequest_w->addHeader("Content-Type: application/llsd+xml"); - - // copied from llurlrequest.cpp - // it appears that apache2.2.3 or django in etch is busted. If - // we do not clear the expect header, we get a 500. May be - // limited to django/mod_wsgi. - curlEasyRequest_w->addHeader("Expect:"); + LLSDSerialize::toXML(body, ostr); + curlEasyRequest_w->setPost(ostr.str().c_str(), ostr.str().length()); } // * Do the action using curl, handle results - lldebugs << "HTTP body: " << body_str << llendl; curlEasyRequest_w->addHeader("Accept: application/llsd+xml"); curlEasyRequest_w->finalizeRequest(url); @@ -500,7 +491,7 @@ static LLSD blocking_request( llwarns << "CURL REQ URL: " << url << llendl; llwarns << "CURL REQ METHOD TYPE: " << method << llendl; llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; - llwarns << "CURL REQ BODY: " << body_str << llendl; + llwarns << "CURL REQ BODY: " << ostr.str() << llendl; llwarns << "CURL HTTP_STATUS: " << http_status << llendl; llwarns << "CURL ERROR: " << curlEasyRequest_w->getErrorString() << llendl; llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 91021186f..c59c5bb3a 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -526,6 +526,7 @@ bool LLURLRequest::configure() curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); rv = true; break; + case HTTP_GET: curlEasyRequest_w->setopt(CURLOPT_HTTPGET, 1); curlEasyRequest_w->setopt(CURLOPT_FOLLOWLOCATION, 1); @@ -538,24 +539,15 @@ bool LLURLRequest::configure() case HTTP_PUT: // Disable the expect http 1.1 extension. POST and PUT default // to turning this on, and I am not too sure what it means. - addHeader("Expect:"); - + curlEasyRequest_w->addHeader("Expect:"); curlEasyRequest_w->setopt(CURLOPT_UPLOAD, 1); curlEasyRequest_w->setopt(CURLOPT_INFILESIZE, bytes); rv = true; break; case HTTP_POST: - // Disable the expect http 1.1 extension. POST and PUT default - // to turning this on, and I am not too sure what it means. - addHeader("Expect:"); - - // Disable the content type http header. - // *FIX: what should it be? - addHeader("Content-Type:"); - // Set the handle for an http post - curlEasyRequest_w->setPost(NULL, bytes); + curlEasyRequest_w->setPost(bytes); // Set Accept-Encoding to allow response compression curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); diff --git a/indra/newview/llcurlrequest.cpp b/indra/newview/llcurlrequest.cpp index 0c566fed2..4084b729f 100644 --- a/indra/newview/llcurlrequest.cpp +++ b/indra/newview/llcurlrequest.cpp @@ -101,7 +101,7 @@ bool Request::post(std::string const& url, headers_t const& headers, std::string llassert_always(success); // AIFIXME: Maybe throw an error. if (!success) return false; - buffered_easy_request_w->setPost(NULL, bytes); + buffered_easy_request_w->setPost(bytes); buffered_easy_request_w->addHeader("Content-Type: application/octet-stream"); buffered_easy_request_w->finalizeRequest(url); @@ -129,7 +129,7 @@ bool Request::post(std::string const& url, headers_t const& headers, LLSD const& LLBufferStream buffer_stream(buffer_w->sChannels, buffer_w->getInput().get()); LLSDSerialize::toXML(data, buffer_stream); S32 bytes = buffer_w->getInput()->countAfter(buffer_w->sChannels.out(), NULL); - buffered_easy_request_w->setPost(NULL, bytes); + buffered_easy_request_w->setPost(bytes); buffered_easy_request_w->addHeader("Content-Type: application/llsd+xml"); buffered_easy_request_w->finalizeRequest(url); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index b1bb2ee0f..227904f90 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -168,8 +168,6 @@ public: LLCurl::TransferInfo mTransferInfo; std::string mURI; - char* mRequestText; - int mRequestTextSize; std::string mProxyAddress; @@ -200,7 +198,6 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { init(request, useGzip); @@ -212,7 +209,6 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, : mCurlEasyRequestStateMachinePtr(NULL), mStatus(LLXMLRPCTransaction::StatusNotStarted), mURI(uri), - mRequestText(0), mResponse(0) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); @@ -223,8 +219,13 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, init(request, useGzip); } - - +// Store pointer to data allocated with XMLRPC_REQUEST_ToXML and call XMLRPC_Free to free it upon destruction. +class AIXMLRPCData : public AIPostField +{ + public: + AIXMLRPCData(char const* allocated_data) : AIPostField(allocated_data) { } + /*virtual*/ ~AIXMLRPCData() { XMLRPC_Free(const_cast(mData)); mData = NULL; } +}; void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { @@ -259,12 +260,11 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); } - mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize); - if (mRequestText) + int requestTextSize; + char* requestText = XMLRPC_REQUEST_ToXML(request, &requestTextSize); + if (requestText) { - Dout(dc::curl, "Writing " << mRequestTextSize << " bytes: \"" << libcwd::buf2str(mRequestText, mRequestTextSize) << "\".");; - curlEasyRequest_w->setopt(CURLOPT_POSTFIELDSIZE, mRequestTextSize); - curlEasyRequest_w->setoptString(CURLOPT_COPYPOSTFIELDS, mRequestText); + curlEasyRequest_w->setPost(new AIXMLRPCData(requestText), requestTextSize); } else { @@ -298,11 +298,6 @@ LLXMLRPCTransaction::Impl::~Impl() { XMLRPC_RequestFree(mResponse, 1); } - - if (mRequestText) - { - XMLRPC_Free(mRequestText); - } } bool LLXMLRPCTransaction::Impl::is_finished(void) const From fa10ce28d28b01f8d59e88840d0cae41131aeedc Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 12 Aug 2012 17:10:44 +0200 Subject: [PATCH 095/123] Missing header --- indra/llmessage/debug_libcurl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llmessage/debug_libcurl.cpp b/indra/llmessage/debug_libcurl.cpp index 3f85dcc2d..40b75d200 100644 --- a/indra/llmessage/debug_libcurl.cpp +++ b/indra/llmessage/debug_libcurl.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "llpreprocessor.h" #include #define COMPILING_DEBUG_LIBCURL_CC From 263a25fb870815eb898e34050b7eec779fe1a150 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 12 Aug 2012 17:42:30 +0200 Subject: [PATCH 096/123] Compile fix for DEBUG_CURLIO --- indra/cwdebug/debug.h | 7 ++++--- indra/llmessage/aicurl.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 862ea263c..a52837411 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -89,16 +89,17 @@ extern LL_COMMON_API fake_channel const notice; } // namespace debug #define LIBCWD_DEBUG_CHANNELS debug -#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print; +#define LibcwDoutScopeBegin(a, b, c) do { using namespace debug; using namespace debug::libcwd; llinfos_nf << print_thread_id << (c).mLabel << ": " << Indent::print; #define LibcwDoutStream llcont #define LibcwDoutScopeEnd llcont << llendl; } while(0) -#define Debug(x) do { using namespace debug; x; } while(0) -#define Dout(a, b) do { using namespace debug; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) +#define Debug(x) do { using namespace debug; using namespace debug::libcwd; x; } while(0) +#define Dout(a, b) do { using namespace debug; using namespace debug::libcwd; if ((a).mOn) { llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << b << llendl; } } while(0) #define DoutEntering(a, b) \ int __slviewer_debug_indentation = 2; \ { \ using namespace debug; \ + using namespace debug::libcwd; \ if ((a).mOn) \ llinfos_nf << print_thread_id << (a).mLabel << ": " << Indent::print << "Entering " << b << llendl; \ else \ diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index ceb1000d5..71d9397d5 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -993,14 +993,14 @@ static int curl_debug_cb(CURL*, curl_infotype infotype, char* buf, size_t size, libcw_do.marker().assign(marker.str().data(), marker.str().size()); if (!debug::channels::dc::curlio.is_on()) debug::channels::dc::curlio.on(); - LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT)) #else if (infotype == CURLINFO_TEXT) { while (size > 0 && (buf[size - 1] == '\r' || buf[size - 1] == '\n')) --size; } - LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcwd::libcw_do, dc::curlio) + LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio) #endif switch (infotype) { From 169670a58b18b10455af2399de7cc9942eb1da9a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 13 Aug 2012 19:30:35 +0200 Subject: [PATCH 097/123] Refurbish last merge. Fix compile for standalone and libcwd. --- indra/llimage/llpngwrapper.h | 5 +++++ indra/llmessage/llares.cpp | 1 + indra/plugins/webkit/media_plugin_webkit.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h index 7def0c713..276709d1a 100644 --- a/indra/llimage/llpngwrapper.h +++ b/indra/llimage/llpngwrapper.h @@ -32,7 +32,12 @@ #ifndef LL_LLPNGWRAPPER_H #define LL_LLPNGWRAPPER_H +#ifdef LL_STANDALONE +#include +#else +// Workaround for wrongly packaged prebuilt. #include "libpng15/png.h" +#endif #include "llimage.h" class LLPngWrapper diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index 48d3f9108..91d13dac3 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -37,6 +37,7 @@ #include "apr_poll.h" #include "llapr.h" +#include "llaprpool.h" #include "llareslistener.h" #if defined(LL_WINDOWS) diff --git a/indra/plugins/webkit/media_plugin_webkit.cpp b/indra/plugins/webkit/media_plugin_webkit.cpp index 9809a9e5e..e64a89d88 100644 --- a/indra/plugins/webkit/media_plugin_webkit.cpp +++ b/indra/plugins/webkit/media_plugin_webkit.cpp @@ -25,8 +25,8 @@ * $/LicenseInfo$ * @endcond */ -#include "llqtwebkit.h" #include "linden_common.h" +#include "llqtwebkit.h" #include "indra_constants.h" // for indra keyboard codes #include "lltimer.h" From 35a53ef1d76c193c3a0031d5d8eb5fa2037b69d0 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 13 Aug 2012 21:59:57 +0200 Subject: [PATCH 098/123] Bug fix (needed for libcwd with memory allocation debugging) --- indra/llmessage/llhttpclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 5e201710f..c8e706f44 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -123,7 +123,7 @@ namespace { public: RawInjector(const U8* data, S32 size) : mData(data), mSize(size) {} - virtual ~RawInjector() {delete mData;} + virtual ~RawInjector() {delete [] mData;} const char* contentType() { return "application/octet-stream"; } From 36ff4dcd11b4e0fe7250fe2bc88ad50053ffb09d Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Tue, 7 Aug 2012 16:48:22 +0200 Subject: [PATCH 099/123] Don't feed empty, non-working callback to CURL Cherry-Pick of 96a4e16a Conflicts: indra/llmessage/llurlrequest.cpp indra/llmessage/llurlrequest.h indra/newview/hipporestrequest.cpp Clear reasons for conflicts, resolved. --- indra/llmessage/llhttpclient.cpp | 9 +------ indra/llmessage/llhttpclient.h | 6 ----- indra/llmessage/llurlrequest.cpp | 40 ++++++------------------------ indra/llmessage/llurlrequest.h | 4 +-- indra/newview/hipporestrequest.cpp | 2 +- 5 files changed, 10 insertions(+), 51 deletions(-) diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index c8e706f44..d611a6b5f 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -41,8 +41,6 @@ const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; -LLURLRequest::SSLCertVerifyCallback LLHTTPClient::mCertVerifyCallback = NULL; - //////////////////////////////////////////////////////////////////////////// // Responder class moved to LLCurl @@ -205,11 +203,6 @@ namespace LLPumpIO* theClientPump = NULL; } -void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback) -{ - LLHTTPClient::mCertVerifyCallback = callback; -} - static void request( const std::string& url, LLURLRequest::ERequestAction method, @@ -244,7 +237,7 @@ static void request( return ; } - req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); + req->checkRootCertificate(true); lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 39a6498d8..d371290db 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -155,12 +155,6 @@ public: ///< for testing static LLPumpIO &getPump(); ///< Hippo special - - static void setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback); - static LLURLRequest::SSLCertVerifyCallback getCertVerifyCallback() { return mCertVerifyCallback; } - -protected: - static LLURLRequest::SSLCertVerifyCallback mCertVerifyCallback; }; #endif // LL_LLHTTPCLIENT_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index c59c5bb3a..ba0d24ce3 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -91,39 +91,6 @@ LLURLRequestDetail::~LLURLRequestDetail() mLastRead = NULL; } -void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) -{ - LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); - mDetail->mSSLVerifyCallback = callback; - AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); - curlEasyRequest_w->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); - curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, true); - curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYHOST, 2); -} - - -// _sslCtxFunction -// Callback function called when an SSL Context is created via CURL -// used to configure the context for custom cert validation - -CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) -{ - LLURLRequest *req = (LLURLRequest *)param; - if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) - { - SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); - return CURLE_OK; - } - SSL_CTX * ctx = (SSL_CTX *) sslctx; - // disable any default verification for server certs - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); - // set the verification callback. - SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); - // the calls are void - return CURLE_OK; - -} - /** * class LLURLRequest */ @@ -195,6 +162,13 @@ void LLURLRequest::addHeader(const char* header) curlEasyRequest_w->addHeader(header); } +void LLURLRequest::checkRootCertificate(bool check) +{ + mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); + mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); +} + + void LLURLRequest::setBodyLimit(U32 size) { mDetail->mBodyLimit = size; diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index aee8a1a84..10ad50f28 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -142,7 +142,7 @@ public: * Set whether request will check that remote server * certificates are signed by a known root CA when using HTTPS. */ - void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); + void checkRootCertificate(bool check); /** @@ -227,8 +227,6 @@ protected: S32 mRequestTransferedBytes; S32 mResponseTransferedBytes; - static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); - // mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic. // Set to false before adding curl request and then only tested. // Reset in removed_from_multi_handle (by another thread), this is thread-safe. diff --git a/indra/newview/hipporestrequest.cpp b/indra/newview/hipporestrequest.cpp index 60d7770a1..12e112bcf 100644 --- a/indra/newview/hipporestrequest.cpp +++ b/indra/newview/hipporestrequest.cpp @@ -268,7 +268,7 @@ static void request(const std::string &url, llwarns << "Failed to create LLURLRequest: " << error.what() << llendl; return; } - req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); + req->checkRootCertificate(true); /* // Insert custom headers if the caller sent any From 19293601b32bd5b2499c12daab280856664c4f8c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 14 Aug 2012 00:45:49 +0200 Subject: [PATCH 100/123] Manual pick of stuff from 'shycurl' (68ad5411) that seemed to make sense. --- indra/llmessage/llhttpclient.cpp | 14 ++++---------- indra/llmessage/llhttpclient.h | 2 +- indra/llmessage/llurlrequest.cpp | 22 +++++++++++----------- indra/llmessage/llurlrequest.h | 7 ++----- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index d611a6b5f..c04bad5e4 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -25,7 +25,6 @@ */ #include "linden_common.h" -#include #include "llhttpclient.h" #include "llassetstorage.h" @@ -39,7 +38,6 @@ #include "message.h" - const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; //////////////////////////////////////////////////////////////////////////// @@ -186,11 +184,9 @@ namespace LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ); S32 fileSize = vfile.getSize(); - U8* fileBuffer; - fileBuffer = new U8 [fileSize]; - vfile.read(fileBuffer, fileSize); - ostream.write((char*)fileBuffer, fileSize); - delete [] fileBuffer; + std::vector fileBuffer(fileSize); + vfile.read(&fileBuffer[0], fileSize); + ostream.write((char*)&fileBuffer[0], fileSize); eos = true; return STATUS_DONE; } @@ -199,7 +195,6 @@ namespace LLAssetType::EType mAssetType; }; - LLPumpIO* theClientPump = NULL; } @@ -209,8 +204,7 @@ static void request( Injector* body_injector, LLCurl::ResponderPtr responder, const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, - const LLSD& headers = LLSD() - ) + const LLSD& headers = LLSD()) { if (responder) { diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index d371290db..ae22fd30a 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -34,10 +34,10 @@ #include #include -#include "llurlrequest.h" #include "llassettype.h" #include "llcurl.h" #include "lliopipe.h" +#include "llurlrequest.h" extern const F32 HTTP_REQUEST_EXPIRY_SECS; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index ba0d24ce3..4948f10d6 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -36,17 +36,17 @@ #include #include #include + #include "llcurl.h" -#include "llfasttimer.h" #include "llioutil.h" #include "llmemtype.h" -#include "llproxy.h" #include "llpumpio.h" #include "llsd.h" #include "llstring.h" #include "apr_env.h" #include "llapr.h" #include "llscopedvolatileaprpool.h" +#include "llfasttimer.h" static const U32 HTTP_STATUS_PIPE_ERROR = 499; /** @@ -73,7 +73,6 @@ public: U32 mBodyLimit; S32 mByteAccumulator; bool mIsBodyLimitSet; - LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; }; LLURLRequestDetail::LLURLRequestDetail() : @@ -81,8 +80,7 @@ LLURLRequestDetail::LLURLRequestDetail() : mLastRead(NULL), mBodyLimit(0), mByteAccumulator(0), - mIsBodyLimitSet(false), - mSSLVerifyCallback(NULL) + mIsBodyLimitSet(false) { } @@ -164,11 +162,11 @@ void LLURLRequest::addHeader(const char* header) void LLURLRequest::checkRootCertificate(bool check) { - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); + AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); + curlEasyRequest_w->setopt(CURLOPT_SSL_VERIFYPEER, check ? 1L : 0L); + curlEasyRequest_w->setoptString(CURLOPT_ENCODING, ""); } - void LLURLRequest::setBodyLimit(U32 size) { mDetail->mBodyLimit = size; @@ -214,10 +212,10 @@ void LLURLRequest::useProxy(bool use_proxy) } } - lldebugs << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = \"" << env_proxy << "\"" << llendl; + LL_DEBUGS("Proxy") << "use_proxy = " << (use_proxy?'Y':'N') << ", env_proxy = " << (!env_proxy.empty() ? env_proxy : "(null)") << LL_ENDL; AICurlEasyRequest_wat curlEasyRequest_w(*mDetail->mCurlEasyRequest); - curlEasyRequest_w->setoptString(CURLOPT_PROXY, use_proxy ? env_proxy : std::string("")); + curlEasyRequest_w->setoptString(CURLOPT_PROXY, (use_proxy && !env_proxy.empty()) ? env_proxy : std::string("")); } void LLURLRequest::useProxy(const std::string &proxy) @@ -319,7 +317,9 @@ LLIOPipe::EStatus LLURLRequest::process_impl( LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; if (!buffer) return STATUS_ERROR; - + + if (!mDetail) return STATUS_ERROR; //Seems to happen on occasion. Need to hunt down why. + // we're still waiting or processing, check how many // bytes we have accumulated. const S32 MIN_ACCUMULATION = 100000; diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index 10ad50f28..fdf7d873e 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -40,7 +40,6 @@ #include "llerror.h" #include "llcurl.h" - extern const std::string CONTEXT_REQUEST; extern const std::string CONTEXT_RESPONSE; extern const std::string CONTEXT_TRANSFERED_BYTES; @@ -144,7 +143,6 @@ public: */ void checkRootCertificate(bool check); - /** * @brief Return at most size bytes of body. * @@ -197,7 +195,6 @@ public: * @brief Give this pipe a chance to handle a generated error */ virtual EStatus handleError(EStatus status, LLPumpIO* pump); - protected: /** @@ -224,8 +221,8 @@ protected: ERequestAction mAction; LLURLRequestDetail* mDetail; LLIOPipe::ptr_t mCompletionCallback; - S32 mRequestTransferedBytes; - S32 mResponseTransferedBytes; + S32 mRequestTransferedBytes; + S32 mResponseTransferedBytes; // mRemoved is used instead of changing mState directly, because I'm not convinced the latter is atomic. // Set to false before adding curl request and then only tested. From ea4d32b4adccd2971cab78369d78ebde1e2d68fe Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 15 Aug 2012 05:43:12 +0200 Subject: [PATCH 101/123] Compile fix. --- indra/llmessage/aicurlprivate.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index a475504b1..4f7e9d6cc 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -123,9 +123,9 @@ class CurlEasyHandle : public boost::noncopyable, protected AICurlEasyHandleEven // Overload for integer types that are too small (libcurl demands a long). CURLcode getinfo(CURLINFO info, S32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } CURLcode getinfo(CURLINFO info, U32* data) { long ldata; CURLcode res = getinfo_priv(info, &ldata); *data = static_cast(ldata); return res; } -#else - CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, static_cast(data)); } - CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, static_cast(data)); } +#else // sizeof(long) == sizeof(int) + CURLcode getinfo(CURLINFO info, S32* data) { return getinfo_priv(info, reinterpret_cast(data)); } + CURLcode getinfo(CURLINFO info, U32* data) { return getinfo_priv(info, reinterpret_cast(data)); } #endif // Perform a file transfer (blocking). From 484fff444f3cc2f13afce8a1274ef61be5c02155 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 31 Aug 2012 05:23:20 +0200 Subject: [PATCH 102/123] Compile fix for OSX --- indra/cwdebug/debug.cc | 4 ---- indra/llcommon/aithreadid.cpp | 2 ++ indra/llcommon/aithreadid.h | 16 +++++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index cad6da46d..06723f8ae 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -447,11 +447,7 @@ std::ostream& operator<<(std::ostream& os, print_thread_id_t) { if (!AIThreadID::in_main_thread_inline()) { -#ifdef LL_DARWIN - os << std::hex << (size_t)apr_os_thread_current() << std::dec << ' '; -#else os << std::hex << (size_t)AIThreadID::getCurrentThread_inline() << std::dec << ' '; -#endif } return os; } diff --git a/indra/llcommon/aithreadid.cpp b/indra/llcommon/aithreadid.cpp index 64a8dd21a..e8dd17097 100644 --- a/indra/llcommon/aithreadid.cpp +++ b/indra/llcommon/aithreadid.cpp @@ -50,6 +50,7 @@ void AIThreadID::set_current_thread_id(void) #endif } +#ifndef LL_DARWIN void AIThreadID::reset(void) { mID = lCurrentThread; @@ -69,6 +70,7 @@ apr_os_thread_t AIThreadID::getCurrentThread(void) { return lCurrentThread; } +#endif std::ostream& operator<<(std::ostream& os, AIThreadID const& id) { diff --git a/indra/llcommon/aithreadid.h b/indra/llcommon/aithreadid.h index 2bb3f1dac..3b3b1500c 100644 --- a/indra/llcommon/aithreadid.h +++ b/indra/llcommon/aithreadid.h @@ -62,11 +62,7 @@ public: friend LL_COMMON_API std::ostream& operator<<(std::ostream& os, AIThreadID const& id); static void set_main_thread_id(void); // Called once to set sMainThreadID. static void set_current_thread_id(void); // Called once for every thread to set lCurrentThread. -#ifdef LL_DARWIN - void reset(void) { mID = apr_os_thread_current(); } - bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } - static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } -#else +#ifndef LL_DARWIN LL_COMMON_API void reset(void); LL_COMMON_API bool equals_current_thread(void) const; LL_COMMON_API static bool in_main_thread(void); @@ -76,6 +72,16 @@ public: bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, lCurrentThread); } static bool in_main_thread_inline(void) { return apr_os_thread_equal(lCurrentThread, sMainThreadID); } static apr_os_thread_t getCurrentThread_inline(void) { return lCurrentThread; } +#else + // Both variants are inline on OS X. + void reset(void) { mID = apr_os_thread_current(); } + void reset_inline(void) { mID = apr_os_thread_current(); } + bool equals_current_thread(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } + bool equals_current_thread_inline(void) const { return apr_os_thread_equal(mID, apr_os_thread_current()); } + static bool in_main_thread(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } + static bool in_main_thread_inline(void) { return apr_os_thread_equal(apr_os_thread_current(), sMainThreadID); } + static apr_os_thread_t getCurrentThread(void) { return apr_os_thread_current(); } + static apr_os_thread_t getCurrentThread_inline(void) { return apr_os_thread_current(); } #endif }; From 21ea923520048e4246596179366bdf36cfc2085c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 31 Aug 2012 05:50:42 +0200 Subject: [PATCH 103/123] Compile fix for OSX --- indra/llmessage/aicurl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 71d9397d5..b3c6f640b 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -127,7 +127,7 @@ void ssl_locking_function(int mode, int n, char const* file, int line) // OpenSSL uniq id function. void ssl_id_function(CRYPTO_THREADID* thread_id) { -#if LL_WINDOWS // apr_os_thread_current() returns a pointer, +#if LL_WINDOWS || LL_DARWIN // apr_os_thread_current() returns a pointer, CRYPTO_THREADID_set_pointer(thread_id, apr_os_thread_current()); #else // else it returns an unsigned long. CRYPTO_THREADID_set_numeric(thread_id, apr_os_thread_current()); From 8051cd4194fa4feec81d55b75fcc95f14fbaa085 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 1 Sep 2012 03:56:52 +0200 Subject: [PATCH 104/123] Install fix for latest 32bitcompatibilitylibs --- indra/newview/viewer_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e966e3b80..f118a68a6 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -872,7 +872,7 @@ class Linux_x86_64Manifest(LinuxManifest): self.end_prefix("lib32") # 32bit libs needed for voice - if self.prefix("../../libraries/x86_64-linux/lib_release_client/32bit-compat", dst="lib32"): + if self.prefix("../../libraries/x86_64-linux/lib/release/32bit-compat", dst="lib32"): self.path("libalut.so") self.path("libidn.so.11") self.path("libopenal.so.1") From a8f5bf8baf227fb910c5ce516f37501c24f619e5 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 5 Sep 2012 02:15:41 +0200 Subject: [PATCH 105/123] Bug fix. dlerror() returns NULL when no errors have occurred since the last call to dlerror() (or since the last call to dlopen(), dlsym() or dlclose()). --- indra/llplugin/llplugininstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llplugin/llplugininstance.cpp b/indra/llplugin/llplugininstance.cpp index a53f67087..19d41f201 100644 --- a/indra/llplugin/llplugininstance.cpp +++ b/indra/llplugin/llplugininstance.cpp @@ -125,7 +125,7 @@ int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_fi buf[0] = 0; if (error) { - strncpy(buf, dlerror(), sizeof(buf)); + strncpy(buf, error, sizeof(buf)); } buf[sizeof(buf) - 1] = 0; } From d8ff809092c632af75d4d3686b3483735469bc35 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 5 Sep 2012 04:12:13 +0200 Subject: [PATCH 106/123] Make webkit work with Qt 4.8 --- indra/plugins/webkit/media_plugin_webkit.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/plugins/webkit/media_plugin_webkit.cpp b/indra/plugins/webkit/media_plugin_webkit.cpp index e64a89d88..6ff524fb9 100644 --- a/indra/plugins/webkit/media_plugin_webkit.cpp +++ b/indra/plugins/webkit/media_plugin_webkit.cpp @@ -160,8 +160,8 @@ private: mVolumeCatcher.pump(); checkEditState(); - - if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) + + if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE) { if(!mInitialNavigateURL.empty()) { @@ -284,7 +284,7 @@ private: bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); if ( result ) { - mInitState = INIT_STATE_INITIALIZED; + setInitState(INIT_STATE_INITIALIZED); // debug spam sent to viewer and displayed in the log as usual postDebugMessage( "browser initialized okay" ); From e3752aefedb9c4495cbfcb85dacb3a8f55aae6b6 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 5 Sep 2012 19:08:58 +0200 Subject: [PATCH 107/123] Fix loading Qt plugin on linux standalone. For standalone there is no need to link libmedia_plugin_webkit.so against Qt's plugins, and thus also not to add "random" paths to LD_LIBRARY_PATH. Just let Qt do it's thing and find and load the plugins it needs itself. --- indra/cmake/WebKitLibPlugin.cmake | 14 +------------- indra/newview/linux_tools/wrapper.sh | 8 ++------ 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake index 7ed824aea..6c8e60103 100644 --- a/indra/cmake/WebKitLibPlugin.cmake +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -14,18 +14,6 @@ if (STANDALONE) "or unset QTDIR if the found Qt is correct.") endif (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") find_package(LLQtWebkit REQUIRED QUIET) - # Add the plugins. - set(QT_PLUGIN_LIBRARIES) - foreach(qlibname qgif qjpeg) - find_library(QT_PLUGIN_${qlibname} ${qlibname} PATHS ${QT_PLUGINS_DIR}/imageformats NO_DEFAULT_PATH) - if (QT_PLUGIN_${qlibname}) - list(APPEND QT_PLUGIN_LIBRARIES ${QT_PLUGIN_${qlibname}}) - else (QT_PLUGIN_${qtlibname}) - message(FATAL_ERROR "Could not find the Qt plugin ${qlibname} in \"${QT_PLUGINS_DIR}/imageformats\"!") - endif (QT_PLUGIN_${qlibname}) - endforeach(qlibname) - # qjpeg depends on libjpeg - list(APPEND QT_PLUGIN_LIBRARIES jpeg) set(WEBKITLIBPLUGIN OFF CACHE BOOL "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") else (STANDALONE) @@ -58,7 +46,7 @@ elseif (DARWIN) ) elseif (LINUX) if (STANDALONE) - set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES} ${QT_PLUGIN_LIBRARIES}) + set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES}) else (STANDALONE) set(WEBKIT_PLUGIN_LIBRARIES llqtwebkit diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh index 04edf2a76..e406e3c71 100755 --- a/indra/newview/linux_tools/wrapper.sh +++ b/indra/newview/linux_tools/wrapper.sh @@ -139,15 +139,11 @@ fi export VIEWER_BINARY='singularity-do-not-run-directly' BINARY_TYPE=$(expr match "$(file -b bin/$VIEWER_BINARY)" '\(.*executable\)') -QPP=qt4/plugins/imageformats/ if [ "${BINARY_TYPE}" == "ELF 64-bit LSB executable" ]; then - QTPLUGINS=/usr/lib64/$QPP:/lib64/$QPP:/usr/local/lib64/$QPP - SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$QTPLUGINS:$LD_LIBRARY_PATH"' + SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib64:`pwd`/lib32:$LD_LIBRARY_PATH"' else - QTPLUGINS=/usr/lib/$QPP:/lib/$QPP:/usr/local/lib/$QPP - SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$QTPLUGINS:$LD_LIBRARY_PATH"' + SL_ENV+='LD_LIBRARY_PATH="`pwd`/lib:$LD_LIBRARY_PATH"' fi - export SL_CMD='$LL_WRAPPER bin/$VIEWER_BINARY' export SL_OPT="`cat gridargs.dat` $@" From c2289c047bddfdd60010b2ed45f06f7d50a32632 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 11 Sep 2012 02:05:54 +0200 Subject: [PATCH 108/123] Only disable TLS1.1 for openssl1.0.1 and higher, leaving SSL3 alone. --- indra/llmessage/aicurl.cpp | 50 +++++++++++++++++++-------------- indra/llmessage/aicurlprivate.h | 3 ++ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index b3c6f640b..32daa7fde 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -42,6 +42,7 @@ #define OPENSSL_THREAD_DEFINES #include // OPENSSL_THREADS #include +#include #include "aicurl.h" #include "llbufferstream.h" @@ -241,13 +242,7 @@ void ssl_init(void) CRYPTO_set_dynlock_create_callback(&ssl_dyn_create_function); CRYPTO_set_dynlock_lock_callback(&ssl_dyn_lock_function); CRYPTO_set_dynlock_destroy_callback(&ssl_dyn_destroy_function); - need_renegotiation_hack = (0x10001000UL <= ssleay && ssleay < 0x10001040); - if (need_renegotiation_hack) - { - llwarns << "This version of libopenssl has a bug that we work around by forcing the TLSv1 protocol. " - "That works on Second Life, but might cause you to fail to login on some OpenSim grids. " - "Upgrade to openssl 1.0.1d or higher to avoid this warning." << llendl; - } + need_renegotiation_hack = (0x10001000UL <= ssleay); llinfos << "Successful initialization of " << SSLeay_version(SSLEAY_VERSION) << " (0x" << std::hex << SSLeay() << ")." << llendl; } @@ -896,7 +891,7 @@ void CurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* us mSSLCtxCallback = callback; mSSLCtxCallbackUserData = userdata; setopt(CURLOPT_SSL_CTX_FUNCTION, callback ? &CurlEasyRequest::SSLCtxCallback : NULL); - setopt(CURLOPT_SSL_CTX_DATA, userdata ? this : NULL); + setopt(CURLOPT_SSL_CTX_DATA, this); } #define llmaybewarns lllog(LLApp::isExiting() ? LLError::LEVEL_INFO : LLError::LEVEL_WARN, NULL, NULL, false, true) @@ -1119,23 +1114,36 @@ void CurlEasyRequest::applyProxySettings(void) } } +//static +CURLcode CurlEasyRequest::curlCtxCallback(CURL* curl, void* sslctx, void* parm) +{ + SSL_CTX* ctx = (SSL_CTX*)sslctx; + // Turn off TLS v1.1 (which is not supported anyway by Linden Lab) because otherwise we fail to connect. + // Also turn off SSL v2, which is highly broken and strongly discouraged[1]. + // [1] http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html#SECURE_RENEGOTIATION + long options = SSL_OP_NO_SSLv2; + if (need_renegotiation_hack) + { + // This option disables openssl to use TLS version 1.1. + // The Linden Lab servers don't support TLS versions later than 1.0, and libopenssl-1.0.1-beta1 up till and including + // libopenssl-1.0.1c have a bug (or feature?) where (re)negotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), + // causing the connection to fail completely without this hack. + // For a commandline test of the same, observe the difference between: + // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which gets no response from the server after sending the initial data, and + // openssl s_client -no_tls1_1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug + // which finishes the negotiation and ends with 'Verify return code: 0 (ok)' + options |= SSL_OP_NO_TLSv1_1; + } + SSL_CTX_set_options(ctx, options); + return CURLE_OK; +} + void CurlEasyRequest::applyDefaultOptions(void) { CertificateAuthority_rat CertificateAuthority_r(gCertificateAuthority); setoptString(CURLOPT_CAINFO, CertificateAuthority_r->file); - if (need_renegotiation_hack) - { - // This option forces openssl to use TLS version 1. - // The Linden Lab servers don't support later TLS versions, and libopenssl-1.0.1-beta1 up till and including - // libopenssl-1.0.1c have a bug where renegotiation fails (see http://rt.openssl.org/Ticket/Display.html?id=2828), - // causing the connection to fail completely without this hack. - // For a commandline test of the same, observe the difference between: - // openssl s_client -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug - // which gets no response from the server after sending the initial data, and - // openssl s_client -tls1 -connect login.agni.lindenlab.com:443 -CAfile packaged/app_settings/CA.pem -debug - // which finishes the negotiation and ends with 'Verify return code: 0 (ok)' - setopt(CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1); - } + setSSLCtxCallback(&curlCtxCallback, NULL); setopt(CURLOPT_NOSIGNAL, 1); // The old code did this for the 'buffered' version, but I think it's nonsense. //setopt(CURLOPT_DNS_CACHE_TIMEOUT, 0); diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index 4f7e9d6cc..1acf08e14 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -253,6 +253,9 @@ class CurlEasyRequest : public CurlEasyHandle { // Called from applyDefaultOptions. void applyProxySettings(void); + // Used in applyProxySettings. + static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm); + public: // Set default options that we want applied to all curl easy handles. void applyDefaultOptions(void); From d57a3b101b0bbecd523ca22ff3117761e2d9d911 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 11 Sep 2012 14:54:51 +0200 Subject: [PATCH 109/123] Hide quilt patch directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c56bf2a54..84a272544 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ /LICENSES/ /edited-files.txt qtcreator-build/ +/.pc From e3460bea091eeb4a85ed8dc3552013a3fcef6adc Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 11 Sep 2012 15:51:18 +0200 Subject: [PATCH 110/123] Compile fix for openssl < 1.0.1 --- indra/llmessage/aicurl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 32daa7fde..a5010b7e6 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1122,6 +1122,7 @@ CURLcode CurlEasyRequest::curlCtxCallback(CURL* curl, void* sslctx, void* parm) // Also turn off SSL v2, which is highly broken and strongly discouraged[1]. // [1] http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html#SECURE_RENEGOTIATION long options = SSL_OP_NO_SSLv2; +#ifdef SSL_OP_NO_TLSv1_1 // Only defined for openssl version 1.0.1 and up. if (need_renegotiation_hack) { // This option disables openssl to use TLS version 1.1. @@ -1135,6 +1136,9 @@ CURLcode CurlEasyRequest::curlCtxCallback(CURL* curl, void* sslctx, void* parm) // which finishes the negotiation and ends with 'Verify return code: 0 (ok)' options |= SSL_OP_NO_TLSv1_1; } +#else + llassert_always(!need_renegotiation_hack); +#endif SSL_CTX_set_options(ctx, options); return CURLE_OK; } From 834a524a672fa135f05a88b06b881a13397b1052 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 11 Sep 2012 17:03:57 +0200 Subject: [PATCH 111/123] Compile fix. This belongs to the latest merge with singu/master. --- indra/newview/statemachine/aistatemachine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index f008a4873..a6a646ec9 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -529,12 +529,12 @@ void AIStateMachine::mainloop(void*) AIStateMachine& statemachine(iter->statemachine()); if (!statemachine.mIdle) { - U64 start = LLFastTimer::getCPUClockCount64(); + U64 start = get_clock_count(); // This might call idle() and then pass the statemachine to another thread who then may call cont(). // Hence, after this isn't not sure what mIdle is, and it can change from true to false at any moment, // if it is true after this function returns. statemachine.multiplex(start); - U64 delta = LLFastTimer::getCPUClockCount64() - start; + U64 delta = get_clock_count() - start; iter->add(delta); total_clocks += delta; if (total_clocks >= max_count) From b5b6d3f85c83dc78a76fac04f989b02cedc5262b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 14 Sep 2012 19:38:05 +0200 Subject: [PATCH 112/123] Fix PluginAttachDebuggerToPlugins for linux. This fixes the problem that many terminals require command line parameters to be passed as command line parameters to the terminal command. The old code would pass the whole gdb command including all it's arguments (whatever replaces %s) as a single argument, regardless of whether there are quotes around the %s or not. Now those arguments are passed as separate arguments if there are no quotes around the %s. Updated documentation with an example on how to open a gdb terminal on a different machines X display. --- indra/llplugin/llpluginprocessparent.cpp | 41 +++++++++++++++++------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index cdb8ec88a..c3fa0fb53 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -379,6 +379,7 @@ void LLPluginProcessParent::idle(void) } else { + // Set PluginAttachDebuggerToPlugins to TRUE to use this. You might also want to set DebugPluginDisableTimeout to TRUE. if(mDebug) { // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. @@ -400,14 +401,37 @@ void LLPluginProcessParent::idle(void) mDebugger.launch(); #elif LL_LINUX // The command we're constructing would look like this on the command line: - // /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345' - // This can be changed by setting the following environment variables, for example: - // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s" + // /usr/bin/xterm -geometry 160x24-0+0 -e /usr/bin/gdb -n /proc/12345/exe 12345 + // Note that most terminals demand that all arguments to the process that is + // started with -e are passed as arguments to the terminal: there are no quotes + // around '/usr/bin/gdb -n /proc/12345/exe 12345'. This is the case for xterm, + // uxterm, konsole etc. The exception might be gnome-terminal. + // + // The constructed command can be changed by setting the following environment + // variables, for example: + // // export LL_DEBUG_GDB_PATH=/usr/bin/gdb + // export LL_DEBUG_TERMINAL_COMMAND='/usr/bin/gnome-terminal --geometry=165x24-0+0 -e "%s"' + // + // Or, as second example, if you are running the viewer on host 'A', and you want + // to open the gdb terminal on the X display of host 'B', you would run on host B: + // 'ssh -X A' (and then start the viewer, or just leave the terminal open), and + // then use: + // + // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/uxterm -fs 9 -fa 'DejaVu Sans Mono' -display localhost:10 -geometry 209x31+0-50 -e %s" + // + // which would open the terminal on B (no quotes around the %s, since this uses uxterm!). + // For a list of available strings to pass to the -fa, run in a terminal: fc-list :scalable=true:spacing=mono: family + char const* env; - std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; + std::string terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb"; cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); + std::string::size_type pos = terminal_command.find("%s"); + if (pos != std::string::npos) + { + terminal_command.replace(pos, 2, cmd.str()); + } typedef boost::tokenizer< boost::escaped_list_separator< char>, std::basic_string< @@ -429,14 +453,7 @@ void LLPluginProcessParent::idle(void) mDebugger.setExecutable(*token); while (++token != tokens.end()) { - if (*token == "%s") - { - mDebugger.addArgument(cmd.str()); - } - else - { - mDebugger.addArgument(*token); - } + mDebugger.addArgument(*token); } mDebugger.launch(); #endif From 5f8eac9dc87755922b979ac687da475d19a4b71d Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sat, 15 Sep 2012 21:15:59 +0200 Subject: [PATCH 113/123] Have i beaten you now, evil extortionists? --- indra/llplugin/slplugin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index ea9afec16..32fd89e9a 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -52,6 +52,7 @@ add_executable(SLPlugin set_target_properties(SLPlugin PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist + LINK_FLAGS "/OPT:NOREF" ) target_link_libraries(SLPlugin From a7b9b0da42b5337528530768ece5ff217b9ba729 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sat, 15 Sep 2012 21:17:16 +0200 Subject: [PATCH 114/123] Version 1.7.2 --- indra/llcommon/llversionviewer.h.in | 2 +- indra/newview/lltexturefetch.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/llversionviewer.h.in b/indra/llcommon/llversionviewer.h.in index 82285523d..40933fcd4 100644 --- a/indra/llcommon/llversionviewer.h.in +++ b/indra/llcommon/llversionviewer.h.in @@ -35,7 +35,7 @@ const S32 LL_VERSION_MAJOR = 1; const S32 LL_VERSION_MINOR = 7; -const S32 LL_VERSION_PATCH = 1; +const S32 LL_VERSION_PATCH = 2; const S32 LL_VERSION_BUILD = ${vBUILD}; const char * const LL_CHANNEL = "${VIEWER_CHANNEL}"; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index f507d22ba..120ed5eb0 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -779,7 +779,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; -// llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; + llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << " URL:"<< mUrl << llendl; if (!mFetcher->mDebugPause) { U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; @@ -2100,7 +2100,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con worker->unlockWorkMutex(); } -// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; + llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } From c67f010302dadeb533202353686e2a7e3244a08d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 18 Sep 2012 05:01:34 +0200 Subject: [PATCH 115/123] Pass filename by reference when possible. --- indra/newview/lllogchat.cpp | 4 ++-- indra/newview/lllogchat.h | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 574d70f4b..e80032239 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -99,7 +99,7 @@ std::string LLLogChat::timestamp(bool withdate) //static -void LLLogChat::saveHistory(std::string filename, std::string line) +void LLLogChat::saveHistory(std::string const& filename, std::string line) { if(!filename.size()) { @@ -120,7 +120,7 @@ void LLLogChat::saveHistory(std::string filename, std::string line) } } -void LLLogChat::loadHistory(std::string filename , void (*callback)(ELogLineType,std::string,void*), void* userdata) +void LLLogChat::loadHistory(std::string const& filename , void (*callback)(ELogLineType,std::string,void*), void* userdata) { if(!filename.size()) { diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index ad903b66f..84f6760ab 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -34,6 +34,8 @@ #ifndef LL_LLLOGCHAT_H #define LL_LLLOGCHAT_H +#include + class LLLogChat { public: @@ -44,9 +46,9 @@ public: LOG_END }; static std::string timestamp(bool withdate = false); - static std::string makeLogFileName(std::string(filename)); - static void saveHistory(std::string filename, std::string line); - static void loadHistory(std::string filename, + static std::string makeLogFileName(std::string filename); + static void saveHistory(std::string const& filename, std::string line); + static void loadHistory(std::string const& filename, void (*callback)(ELogLineType,std::string,void*), void* userdata); private: From b7f5d7e4153bc9c6650ac8d398379737eedefe88 Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Thu, 20 Sep 2012 01:37:40 +0200 Subject: [PATCH 116/123] Windows build fixes --- indra/llmessage/aicurl.cpp | 3 +++ indra/llwindow/llwindowwin32.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index a5010b7e6..8237fcaa4 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -39,6 +39,9 @@ #include "linden_common.h" +#if LL_WINDOWS +#include //remove classic winsock from windows.h +#endif #define OPENSSL_THREAD_DEFINES #include // OPENSSL_THREADS #include diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 70c6f771f..53ba137d0 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1416,7 +1416,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO { found_format = TRUE; } - else if(cur_format >= num_formats-1) + else if(cur_format >= (S32)num_formats-1) { cur_format = 0; found_format = TRUE; From 7d2f2bd6104468c28ef2f5b90170c7df9098b82d Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 16:25:44 -0400 Subject: [PATCH 117/123] Fixed python indentation in newview --- indra/newview/build_win32_appConfig.py | 20 +-- indra/newview/viewer_manifest.py | 214 ++++++++++++------------- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/indra/newview/build_win32_appConfig.py b/indra/newview/build_win32_appConfig.py index 44b63afce..cb1df6580 100644 --- a/indra/newview/build_win32_appConfig.py +++ b/indra/newview/build_win32_appConfig.py @@ -2,9 +2,9 @@ # @brief Create the windows app.config file to redirect crt linkage. # # $LicenseInfo:firstyear=2009&license=viewergpl$ -# +# # Copyright (c) 2009, Linden Research, Inc. -# +# # Second Life Viewer Source Code # The source code in this file ("Source Code") is provided by Linden Lab # to you under the terms of the GNU General Public License, version 2.0 @@ -12,17 +12,17 @@ # ("Other License"), formally executed by you and Linden Lab. Terms of # the GPL can be found in doc/GPL-license.txt in this distribution, or # online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 -# +# # There are special exceptions to the terms and conditions of the GPL as # it is applied to this Source Code. View the full text of the exception # in the file doc/FLOSS-exception.txt in this software distribution, or # online at # http://secondlifegrid.net/programs/open_source/licensing/flossexception -# +# # By copying, modifying or distributing this software, you acknowledge # that you have read and understood your obligations described above, # and agree to abide by those obligations. -# +# # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, # COMPLETENESS OR PERFORMANCE. @@ -35,23 +35,23 @@ def main(): src_manifest_name = sys.argv[1] src_config_name = sys.argv[2] dst_config_name = sys.argv[3] - + manifest_dom = parse(src_manifest_name) node = manifest_dom.getElementsByTagName('assemblyIdentity')[0] manifest_assm_ver = node.getAttribute('version') - + config_dom = parse(src_config_name) node = config_dom.getElementsByTagName('bindingRedirect')[0] node.setAttribute('newVersion', manifest_assm_ver) - src_old_ver=re.match('([^-]*-).*', node.getAttribute('oldVersion')).group(1) + src_old_ver=re.match('([^-]*-).*', node.getAttribute('oldVersion')).group(1) node.setAttribute('oldVersion', src_old_ver + manifest_assm_ver) comment = config_dom.createComment("This file is automatically generated by the build. see indra/newview/build_win32_appConfig.py") config_dom.insertBefore(comment, config_dom.childNodes[0]) - + f = open(dst_config_name, 'w') config_dom.writexml(f) f.close() - + return 0 if __name__ == "__main__": diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 84e6ff3cc..576488cb5 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -5,9 +5,9 @@ # them into installers for all supported platforms. # # $LicenseInfo:firstyear=2006&license=viewergpl$ -# +# # Copyright (c) 2006-2009, Linden Research, Inc. -# +# # Second Life Viewer Source Code # The source code in this file ("Source Code") is provided by Linden Lab # to you under the terms of the GNU General Public License, version 2.0 @@ -15,17 +15,17 @@ # ("Other License"), formally executed by you and Linden Lab. Terms of # the GPL can be found in doc/GPL-license.txt in this distribution, or # online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 -# +# # There are special exceptions to the terms and conditions of the GPL as # it is applied to this Source Code. View the full text of the exception # in the file doc/FLOSS-exception.txt in this software distribution, or # online at # http://secondlifegrid.net/programs/open_source/licensing/flossexception -# +# # By copying, modifying or distributing this software, you acknowledge # that you have read and understood your obligations described above, # and agree to abide by those obligations. -# +# # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, # COMPLETENESS OR PERFORMANCE. @@ -76,36 +76,36 @@ class ViewerManifest(LLManifest): # skins if self.prefix(src="skins"): - self.path("paths.xml") - # include the entire textures directory recursively - if self.prefix(src="default/textures"): - self.path("*.tga") - self.path("*.j2c") - self.path("*.jpg") - self.path("*.png") - self.path("textures.xml") - self.end_prefix("default/textures") - self.path("default/xui/*/*.xml") - self.path("Default.xml") - self.path("default/*.xml") - if self.prefix(src="dark/textures"): - self.path("*.tga") - self.path("*.j2c") - self.path("*.jpg") - self.path("*.png") - self.path("textures.xml") - self.end_prefix("dark/textures") - self.path("dark.xml") - self.path("dark/*.xml") - - # Local HTML files (e.g. loading screen) - if self.prefix(src="*/html"): - self.path("*.png") - self.path("*/*/*.html") - self.path("*/*/*.gif") - self.end_prefix("*/html") - self.end_prefix("skins") - + self.path("paths.xml") + # include the entire textures directory recursively + if self.prefix(src="default/textures"): + self.path("*.tga") + self.path("*.j2c") + self.path("*.jpg") + self.path("*.png") + self.path("textures.xml") + self.end_prefix("default/textures") + self.path("default/xui/*/*.xml") + self.path("Default.xml") + self.path("default/*.xml") + if self.prefix(src="dark/textures"): + self.path("*.tga") + self.path("*.j2c") + self.path("*.jpg") + self.path("*.png") + self.path("textures.xml") + self.end_prefix("dark/textures") + self.path("dark.xml") + self.path("dark/*.xml") + + # Local HTML files (e.g. loading screen) + if self.prefix(src="*/html"): + self.path("*.png") + self.path("*/*/*.html") + self.path("*/*/*.gif") + self.end_prefix("*/html") + self.end_prefix("skins") + # Files in the newview/ directory self.path("gpu_table.txt") @@ -158,7 +158,7 @@ class ViewerManifest(LLManifest): else: channel_flags = '--channel "%s"' % self.channel() - # Deal with settings + # Deal with settings if self.default_channel() and self.default_grid(): setting_flags = '' elif self.default_grid(): @@ -167,7 +167,7 @@ class ViewerManifest(LLManifest): else: setting_flags = '--settings settings_%s_%s.xml'\ % (self.grid(), self.channel_lowerword()) - + return " ".join((channel_flags, grid_flags, setting_flags)).strip() class WindowsManifest(ViewerManifest): @@ -185,11 +185,11 @@ class WindowsManifest(ViewerManifest): self.path(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration'], "SLPlugin.exe"), "SLPlugin.exe") - - # need to get the kdu dll from any of the build directories as well + + # need to get the kdu dll from any of the build directories as well #~ try: #~ self.path(self.find_existing_file('../llkdu/%s/llkdu.dll' % self.args['configuration'], - #~ '../../libraries/i686-win32/lib/release/llkdu.dll'), + #~ '../../libraries/i686-win32/lib/release/llkdu.dll'), #~ dst='llkdu.dll') #~ pass #~ except: @@ -210,10 +210,10 @@ class WindowsManifest(ViewerManifest): # For using FMOD for sound... DJS #~if self.prefix(src="../../libraries/i686-win32/lib/release", dst=""): #~try: - #~self.path("fmod.dll") - #~pass + #~self.path("fmod.dll") + #~pass #~except: - #~print "Skipping fmod.dll - not found" + #~print "Skipping fmod.dll - not found" #~ pass #~self.end_prefix() @@ -236,7 +236,7 @@ class WindowsManifest(ViewerManifest): if self.prefix(src='../plugins/webkit/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_webkit.dll") self.end_prefix() - + # For WebKit/Qt plugin runtimes if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"): self.path("libeay32.dll") @@ -268,47 +268,47 @@ class WindowsManifest(ViewerManifest): # Get llcommon and deps. If missing assume static linkage and continue. if self.prefix(src=self.args['configuration'], dst=""): - try: - self.path('llcommon.dll') - except RuntimeError, err: - print err.message - print "Skipping llcommon.dll (assuming llcommon was linked statically)" - self.end_prefix() + try: + self.path('llcommon.dll') + except RuntimeError, err: + print err.message + print "Skipping llcommon.dll (assuming llcommon was linked statically)" + self.end_prefix() if self.prefix(src="../../libraries/i686-win32/lib/release", dst=""): - self.path("libeay32.dll") - self.path("ssleay32.dll") - try: - self.path('libapr-1.dll') - self.path('libaprutil-1.dll') - self.path('libapriconv-1.dll') - except RuntimeError, err: - pass - self.end_prefix() - + self.path("libeay32.dll") + self.path("ssleay32.dll") + try: + self.path('libapr-1.dll') + self.path('libaprutil-1.dll') + self.path('libapriconv-1.dll') + except RuntimeError, err: + pass + self.end_prefix() + # For google-perftools tcmalloc allocator. self.path("../../libraries/i686-win32/lib/release/libtcmalloc_minimal.dll", dst="libtcmalloc_minimal.dll") - + try: - if self.prefix("../../libraries/i686-win32/lib/release/msvcrt", dst=""): - self.path("*.dll") - self.path("*.manifest") - self.end_prefix() + if self.prefix("../../libraries/i686-win32/lib/release/msvcrt", dst=""): + self.path("*.dll") + self.path("*.manifest") + self.end_prefix() except: - pass - - + pass + + # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx #~ if self.prefix(src=self.args['configuration'], dst=""): - #~ if self.args['configuration'] == 'Debug': - #~ self.path("msvcr80d.dll") - #~ self.path("msvcp80d.dll") - #~ self.path("Microsoft.VC80.DebugCRT.manifest") - #~ else: - #~ self.path("msvcr80.dll") - #~ self.path("msvcp80.dll") - #~ self.path("Microsoft.VC80.CRT.manifest") - #~ self.end_prefix() + #~ if self.args['configuration'] == 'Debug': + #~ self.path("msvcr80d.dll") + #~ self.path("msvcp80d.dll") + #~ self.path("Microsoft.VC80.DebugCRT.manifest") + #~ else: + #~ self.path("msvcr80.dll") + #~ self.path("msvcp80.dll") + #~ self.path("Microsoft.VC80.CRT.manifest") + #~ self.end_prefix() # The config file name needs to match the exe's name. #~ self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config") @@ -432,16 +432,16 @@ class WindowsManifest(ViewerManifest): # We use the Unicode version of NSIS, available from # http://www.scratchpaper.com/ try: - import _winreg as reg - NSIS_path = reg.QueryValue(reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\NSIS\Unicode") + '\\makensis.exe' - self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) + import _winreg as reg + NSIS_path = reg.QueryValue(reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\NSIS\Unicode") + '\\makensis.exe' + self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) except: - try: - NSIS_path = os.environ['ProgramFiles'] + '\\NSIS\\Unicode\\makensis.exe' - self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) - except: - NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe' - self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) + try: + NSIS_path = os.environ['ProgramFiles'] + '\\NSIS\\Unicode\\makensis.exe' + self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) + except: + NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe' + self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) # self.remove(self.dst_path_of(tempfile)) # If we're on a build machine, sign the code using our Authenticode certificate. JC sign_py = os.path.expandvars("{SIGN_PY}") @@ -479,7 +479,7 @@ class DarwinManifest(ViewerManifest): self.path("featuretable_mac.txt") self.path("SecondLife.nib") - # SG:TODO + # SG:TODO self.path("../newview/res/singularity.icns", dst="singularity.icns") # Translations @@ -507,8 +507,8 @@ class DarwinManifest(ViewerManifest): self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib") self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice") - self.path("../llcommon/" + self.args['configuration'] + "/libllcommon.dylib", "libllcommon.dylib") - + self.path("../llcommon/" + self.args['configuration'] + "/libllcommon.dylib", "libllcommon.dylib") + libfile = "lib%s.dylib" libdir = "../../libraries/universal-darwin/lib/release" @@ -535,14 +535,14 @@ class DarwinManifest(ViewerManifest): # our apps try: - self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") - self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") + self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") + self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") except: - pass + pass # plugin launcher self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app") - + # dependencies on shared libs mac_crash_logger_res_path = self.dst_path_of("mac-crash-logger.app/Contents/Resources") slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources") @@ -567,7 +567,7 @@ class DarwinManifest(ViewerManifest): self.path("../plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") self.path("../../libraries/universal-darwin/lib/release/libllqtwebkit.dylib", "libllqtwebkit.dylib") - self.end_prefix("llplugin") + self.end_prefix("llplugin") # command line arguments for connecting to the proper grid self.put_in_file(self.flags_list(), 'arguments.txt') @@ -581,14 +581,14 @@ class DarwinManifest(ViewerManifest): # arguments yields a slightly smaller binary but makes crash logs mostly useless. # This may be desirable for the final release. Or not. if self.buildtype().lower()=='release': - if ("package" in self.args['actions'] or + if ("package" in self.args['actions'] or "unpacked" in self.args['actions']): self.run_command('strip -S "%(viewer_binary)s"' % { 'viewer_binary' : self.dst_path_of('Contents/MacOS/'+self.app_name())}) def app_name(self): return "Singularity" - + def info_plist_name(self): return "Info-Singularity.plist" @@ -638,14 +638,14 @@ class DarwinManifest(ViewerManifest): # one for release candidate and one for first look. Any other channels # will use the release .DS_Store, and will look broken. # - Ambroff 2008-08-20 - # Added a .DS_Store for snowglobe - Merov 2009-06-17 - - # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different + # Added a .DS_Store for snowglobe - Merov 2009-06-17 + + # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different if (self.app_name()=="Snowglobe"): dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg') else: dmg_template = os.path.join( - 'installers', + 'installers', 'darwin', '%s-dmg' % "".join(self.channel_unique().split()).lower()) @@ -686,7 +686,7 @@ class LinuxManifest(ViewerManifest): def construct(self): super(LinuxManifest, self).construct() self.path("licenses-linux.txt","licenses.txt") - + self.path("res/"+self.icon_name(),self.icon_name()) if self.prefix("linux_tools", dst=""): self.path("client-readme.txt","README-linux.txt") @@ -728,7 +728,7 @@ class LinuxManifest(ViewerManifest): def binary_name(self): return 'singularity-do-not-run-directly' - + def icon_name(self): return "singularity_icon.png" @@ -826,12 +826,12 @@ class Linux_i686Manifest(LinuxManifest): # Vivox runtimes if self.prefix(src="vivox-runtime/i686-linux", dst="bin"): - self.path("SLVoice") - self.end_prefix() + self.path("SLVoice") + self.end_prefix() if self.prefix(src="vivox-runtime/i686-linux", dst="lib"): - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.end_prefix("lib") + self.path("libortp.so") + self.path("libvivoxsdk.so") + self.end_prefix("lib") class Linux_x86_64Manifest(LinuxManifest): From 0287e21c01e40ec76b8d18d0bfb22a8b0b3f0dcc Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 16:34:10 -0400 Subject: [PATCH 118/123] More python indentation fixing...DIE TABS DIE --- scripts/install.py | 110 +++++++++++++------------- scripts/public_fetch_tarballs.py | 15 ++-- scripts/template_verifier.py | 16 ++-- scripts/update_source_contributors.py | 5 +- scripts/update_version_files.py | 15 ++-- 5 files changed, 78 insertions(+), 83 deletions(-) diff --git a/scripts/install.py b/scripts/install.py index e53f664b4..e027c7322 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -134,12 +134,12 @@ class InstallFile(object): print "Found matching package:", self.filename return print "Downloading",self.url,"to local file",self.filename - + request = urllib2.Request(self.url) - + if re.match("/^http:\/\/github.com/", self.url): - request.add_header('User-agent', defaultUserAgent) - + request.add_header('User-agent', defaultUserAgent) + file(self.filename, 'wb').write(urllib2.urlopen(request).read()) if self.md5sum and not self._is_md5sum_match(): raise RuntimeError("Error matching md5 for %s" % self.url) @@ -379,7 +379,7 @@ class Installer(object): def _update_installable(self, name, platform, url, md5sum): """Update installable entry with specific package information. - @param installable[in,out] a dict containing installable details. + @param installable[in,out] a dict containing installable details. @param platform Platform info, i.e. linux/i686, windows/i686 etc. @param url URL of tar file @param md5sum md5sum of tar file @@ -420,16 +420,16 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" print "Updating installable '" + name + "'." for arg in ('platform', 'url', 'md5sum'): if not kwargs[arg]: - if arg == 'platform': + if arg == 'platform': print platform_help_str kwargs[arg] = raw_input("Package "+arg+":") #path = kwargs['platform'].split('/') - return self._update_installable(name, kwargs['platform'], + return self._update_installable(name, kwargs['platform'], kwargs['url'], kwargs['md5sum']) def add_installable_metadata(self, name, **kwargs): - """Interactively add (only) library metadata into install, + """Interactively add (only) library metadata into install, w/o adding installable""" if name not in self._installables: print "Adding installable '" + name + "'." @@ -531,8 +531,8 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" """ ifiles = [] for bin in self._installables: - ifiles.extend(self._installables[bin].ifiles(bin, - platform, + ifiles.extend(self._installables[bin].ifiles(bin, + platform, cache_dir)) to_install = [] to_uninstall = [] @@ -579,7 +579,7 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" target = os.path.basename(tfile) soname = os.popen("readelf -d \"%(install_dir)s/%(tfile)s\" %(stderr_redirect)s" " | grep SONAME | sed -e 's/.*\[//;s/\].*//'" % - {"install_dir": install_dir, "tfile": tfile, "stderr_redirect": ("2>/dev/null" if self._dryrun else "")}).read() + {"install_dir": install_dir, "tfile": tfile, "stderr_redirect": ("2>/dev/null" if self._dryrun else "")}).read() soname = soname.strip() if soname: # not empty tmpfname = os.path.dirname(LINK) + "/" + soname @@ -634,13 +634,13 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" self.uninstall(to_uninstall, install_dir) self._install(to_install, install_dir) - def do_install(self, installables, platform, install_dir, cache_dir=None, + def do_install(self, installables, platform, install_dir, cache_dir=None, check_license=True, scp=None): """Determine what installables should be installed. If they were passed in on the command line, use them, otherwise install all known installables. """ - if not cache_dir: + if not cache_dir: cache_dir = _default_installable_cache() all_installables = self.list_installables() if not len(installables): @@ -651,7 +651,7 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" install_installables = installables for installable in install_installables: if installable not in all_installables: - raise RuntimeError('Unknown installable: %s' % + raise RuntimeError('Unknown installable: %s' % (installable,)) if check_license: # *TODO: check against a list of 'known good' licenses. @@ -660,13 +660,13 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" for installable in install_installables: if not self.is_valid_license(installable): return 1 - + # Set up the 'scp' handler opener = urllib2.build_opener() scp_or_http = SCPOrHTTPHandler(scp) opener.add_handler(scp_or_http) urllib2.install_opener(opener) - + # Do the work of installing the requested installables. self.install( install_installables, @@ -680,7 +680,7 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" if pkg not in self._installed: raise RuntimeError("No '%s' available for '%s'." % (pkg, platform)) - + def do_uninstall(self, installables, install_dir): # Do not bother to check license if we're uninstalling. all_installed = self.list_installed() @@ -692,7 +692,7 @@ windows/i686/vs/2003 -- specify a windows visual studio 2003 package""" uninstall_installables = installables for installable in uninstall_installables: if installable not in all_installed: - raise RuntimeError('Installable not installed: %s' % + raise RuntimeError('Installable not installed: %s' % (installable,)) self.uninstall(uninstall_installables, install_dir) @@ -815,8 +815,8 @@ def _get_platform(): this_platform = platform_map[sys.platform] if this_platform == 'linux': if platform.architecture()[0] == '64bit': - # TODO -- someday when install.py accepts a platform of the form - # os/arch/compiler/compiler_version then we can replace the + # TODO -- someday when install.py accepts a platform of the form + # os/arch/compiler/compiler_version then we can replace the # 'linux64' platform with 'linux/x86_64/gcc/4.1' this_platform = 'linux64' return this_platform @@ -832,7 +832,7 @@ def _getuser(): return win32api.GetUserName() def _default_installable_cache(): - """In general, the installable files do not change much, so find a + """In general, the installable files do not change much, so find a host/user specific location to cache files.""" user = _getuser() cache_dir = "/var/tmp/%s/install.cache" % user @@ -882,75 +882,75 @@ linux/x86_64/gcc/4.0 darwin/universal/gcc/4.0 """) parser.add_option( - '--dry-run', + '--dry-run', action='store_true', default=False, dest='dryrun', help='Do not actually install files. Downloads will still happen.') parser.add_option( - '--install-manifest', + '--install-manifest', type='string', default=os.path.join(base_dir, 'install.xml'), dest='install_filename', help='The file used to describe what should be installed.') parser.add_option( - '--installed-manifest', + '--installed-manifest', type='string', default=os.path.join(base_dir, 'installed.xml'), dest='installed_filename', help='The file used to record what is installed.') parser.add_option( - '--export-manifest', + '--export-manifest', action='store_true', default=False, dest='export_manifest', help="Print the install manifest to stdout and exit.") parser.add_option( - '-p', '--platform', + '-p', '--platform', type='string', default=_get_platform(), dest='platform', help="""Override the automatically determined platform. \ You can specify 'all' to do a installation of installables for all platforms.""") parser.add_option( - '--cache-dir', + '--cache-dir', type='string', default=_default_installable_cache(), dest='cache_dir', help='Where to download files. Default: %s'% \ (_default_installable_cache())) parser.add_option( - '--install-dir', + '--install-dir', type='string', default=base_dir, dest='install_dir', help='Where to unpack the installed files.') parser.add_option( - '--list-installed', + '--list-installed', action='store_true', default=False, dest='list_installed', help="List the installed package names and exit.") parser.add_option( - '--skip-license-check', + '--skip-license-check', action='store_false', default=True, dest='check_license', help="Do not perform the license check.") parser.add_option( - '--list-licenses', + '--list-licenses', action='store_true', default=False, dest='list_licenses', help="List known licenses and exit.") parser.add_option( - '--detail-license', + '--detail-license', type='string', default=None, dest='detail_license', help="Get detailed information on specified license and exit.") parser.add_option( - '--add-license', + '--add-license', type='string', default=None, dest='new_license', @@ -959,114 +959,114 @@ license. Specify --license-url if the license is remote or specify \ --license-text, otherwse the license text will be read from standard \ input.""") parser.add_option( - '--license-url', + '--license-url', type='string', default=None, dest='license_url', help="""Put the specified url into an added license. \ Ignored if --add-license is not specified.""") parser.add_option( - '--license-text', + '--license-text', type='string', default=None, dest='license_text', help="""Put the text into an added license. \ Ignored if --add-license is not specified.""") parser.add_option( - '--remove-license', + '--remove-license', type='string', default=None, dest='remove_license', help="Remove a named license.") parser.add_option( - '--remove-installable', + '--remove-installable', type='string', default=None, dest='remove_installable', help="Remove a installable from the install file.") parser.add_option( - '--add-installable', + '--add-installable', type='string', default=None, dest='add_installable', - help="""Add a installable into the install file. Argument is \ + help="""Add a installable into the install file. Argument is \ the name of the installable to add.""") parser.add_option( - '--add-installable-metadata', + '--add-installable-metadata', type='string', default=None, dest='add_installable_metadata', help="""Add package for library into the install file. Argument is \ the name of the library to add.""") parser.add_option( - '--installable-copyright', + '--installable-copyright', type='string', default=None, dest='installable_copyright', help="""Copyright for specified new package. Ignored if \ --add-installable is not specified.""") parser.add_option( - '--installable-license', + '--installable-license', type='string', default=None, dest='installable_license', help="""Name of license for specified new package. Ignored if \ --add-installable is not specified.""") parser.add_option( - '--installable-description', + '--installable-description', type='string', default=None, dest='installable_description', help="""Description for specified new package. Ignored if \ --add-installable is not specified.""") parser.add_option( - '--add-installable-package', + '--add-installable-package', type='string', default=None, dest='add_installable_package', help="""Add package for library into the install file. Argument is \ the name of the library to add.""") parser.add_option( - '--package-platform', + '--package-platform', type='string', default=None, dest='package_platform', help="""Platform for specified new package. \ Ignored if --add-installable or --add-installable-package is not specified.""") parser.add_option( - '--package-url', + '--package-url', type='string', default=None, dest='package_url', help="""URL for specified package. \ Ignored if --add-installable or --add-installable-package is not specified.""") parser.add_option( - '--package-md5', + '--package-md5', type='string', default=None, dest='package_md5', help="""md5sum for new package. \ Ignored if --add-installable or --add-installable-package is not specified.""") parser.add_option( - '--list', + '--list', action='store_true', default=False, dest='list_installables', help="List the installables in the install manifest and exit.") parser.add_option( - '--detail', + '--detail', type='string', default=None, dest='detail_installable', help="Get detailed information on specified installable and exit.") parser.add_option( - '--detail-installed', + '--detail-installed', type='string', default=None, dest='detail_installed', help="Get list of files for specified installed installable and exit.") parser.add_option( - '--uninstall', + '--uninstall', action='store_true', default=False, dest='uninstall', @@ -1074,7 +1074,7 @@ Ignored if --add-installable or --add-installable-package is not specified.""") during installation, if no installables are listed then all installed \ installables are removed.""") parser.add_option( - '--scp', + '--scp', type='string', default='scp', dest='scp', @@ -1179,9 +1179,9 @@ def main(): elif options.uninstall: installer.do_uninstall(args, options.install_dir) else: - installer.do_install(args, options.platform, options.install_dir, - options.cache_dir, options.check_license, - options.scp) + installer.do_install(args, options.platform, options.install_dir, + options.cache_dir, options.check_license, + options.scp) # save out any changes installer.save() diff --git a/scripts/public_fetch_tarballs.py b/scripts/public_fetch_tarballs.py index 7f7439108..455a42549 100755 --- a/scripts/public_fetch_tarballs.py +++ b/scripts/public_fetch_tarballs.py @@ -134,8 +134,8 @@ def extract_tarball_sans_linden(filename, targetdir): archive.extract(tarentry) archive.close() -# Extract either .tar.gz file or .zip file to targetdir, stripping off the -# leading "linden" directory, but leaving the directory structure otherwise +# Extract either .tar.gz file or .zip file to targetdir, stripping off the +# leading "linden" directory, but leaving the directory structure otherwise # intact. def extract_archive_sans_linden(filename, targetdir): if(filename.endswith('.tar.gz')): @@ -143,7 +143,7 @@ def extract_archive_sans_linden(filename, targetdir): elif(filename.endswith('.zip')): extract_zipfile_sans_linden(filename, targetdir) else: - raise Exception, "Unhandled archive type" + raise Exception, "Unhandled archive type" def get_assetnames_by_platform(platform): assetnames=['SLASSET_ART'] @@ -170,7 +170,7 @@ def _get_platform(): # copied from install.py def _default_installable_cache(): - """In general, the installable files do not change much, so find a + """In general, the installable files do not change much, so find a host/user specific location to cache files.""" user = _getuser() cache_dir = "/var/tmp/%s/install.cache" % user @@ -215,7 +215,7 @@ Example: the corresponding asset bundle into your source tree. """) parser.add_option( - '-p', '--platform', + '-p', '--platform', type='choice', default=_get_platform(), dest='platform', @@ -225,14 +225,14 @@ You can specify 'all' to get assets for all platforms. Choices: windows, \ darwin, linux, solaris, or all. Default: autodetected (%s)""" % \ _get_platform()) parser.add_option( - '--cache-dir', + '--cache-dir', type='string', default=_default_installable_cache(), dest='cache_dir', help='Where to download files. Default: %s'% \ (_default_installable_cache())) parser.add_option( - '--install-dir', + '--install-dir', type='string', default=base_dir, dest='install_dir', @@ -278,4 +278,3 @@ def main(argv): # execute main() only if invoked directly: if __name__ == "__main__": sys.exit(main(sys.argv)) - diff --git a/scripts/template_verifier.py b/scripts/template_verifier.py index 8bb5e1d76..cc112d130 100755 --- a/scripts/template_verifier.py +++ b/scripts/template_verifier.py @@ -78,8 +78,8 @@ from indra.ipc import tokenstream from indra.ipc import llmessage def getstatusall(command): - """ Like commands.getstatusoutput, but returns stdout and - stderr separately(to get around "killed by signal 15" getting + """ Like commands.getstatusoutput, but returns stdout and + stderr separately(to get around "killed by signal 15" getting included as part of the file). Also, works on Windows.""" (input, out, err) = os.popen3(command, 't') status = input.close() # send no input to the command @@ -103,7 +103,7 @@ MESSAGE_TEMPLATE = 'message_template.msg' PRODUCTION_ACCEPTABLE = (compatibility.Same, compatibility.Newer) DEVELOPMENT_ACCEPTABLE = ( compatibility.Same, compatibility.Newer, - compatibility.Older, compatibility.Mixed) + compatibility.Older, compatibility.Mixed) MAX_MASTER_AGE = 60 * 60 * 4 # refresh master cache every 4 hours @@ -146,7 +146,7 @@ def fetch(url): return open(file_name).read() else: # *FIX: this doesn't throw an exception for a 404, and oddly enough the sl.com 404 page actually gets parsed successfully - return ''.join(urllib.urlopen(url).readlines()) + return ''.join(urllib.urlopen(url).readlines()) def cache_master(master_url): """Using the url for the master, updates the local cache, and returns an url to the local cache.""" @@ -251,7 +251,7 @@ http://wiki.secondlife.com/wiki/Template_verifier.py elif len(args) == 1: master_url = None current_filename = args[0] - print "master:", options.master_url + print "master:", options.master_url print "current:", current_filename current_url = 'file://%s' % current_filename # nothing specified, use defaults for everything @@ -263,7 +263,7 @@ http://wiki.secondlife.com/wiki/Template_verifier.py if master_url is None: master_url = options.master_url - + if current_url is None: current_filename = local_template_filename() print "master:", options.master_url @@ -291,7 +291,7 @@ http://wiki.secondlife.com/wiki/Template_verifier.py print "Syntax-checking the local template ONLY, no compatibility check is being run." print "Cause: %s\n\n" % e return 0 - + acceptable, compat = compare( master_parsed, current_parsed, options.mode) @@ -308,5 +308,3 @@ http://wiki.secondlife.com/wiki/Template_verifier.py if __name__ == '__main__': sys.exit(run(sys.argv[1:])) - - diff --git a/scripts/update_source_contributors.py b/scripts/update_source_contributors.py index 6d8fceeaf..a37a6b2dc 100755 --- a/scripts/update_source_contributors.py +++ b/scripts/update_source_contributors.py @@ -2,7 +2,7 @@ # -*- coding: UTF-8 -*- """\ @file update_source_contributors.py -@brief Update the source contributor list in one place, instead of having +@brief Update the source contributor list in one place, instead of having to substitute by hand. Derived from update_viewer_version.py $LicenseInfo:firstyear=2006&license=viewergpl$ @@ -98,7 +98,7 @@ Options: --verbose --help Print this message and exit. - + """ def _getstatusoutput(cmd): """Return Win32 (status, output) of executing cmd @@ -200,4 +200,3 @@ def main(): return 0 main() - diff --git a/scripts/update_version_files.py b/scripts/update_version_files.py index 57fc45662..864a154ed 100755 --- a/scripts/update_version_files.py +++ b/scripts/update_version_files.py @@ -1,7 +1,7 @@ #!/usr/bin/python """\ @file update_version_files.py -@brief Update all of the various files in the repository to a new +@brief Update all of the various files in the repository to a new version number, instead of having to figure it out by hand $LicenseInfo:firstyear=2006&license=viewergpl$ @@ -96,20 +96,20 @@ Common Uses: update_version_files.py --skip-on-branch='^Branch_' # Update server and viewer version numbers explicitly: - update_version_files.py --version=1.18.1.6 - + update_version_files.py --version=1.18.1.6 + # Update just the viewer version number explicitly: - update_version_files.py --viewer --version=1.18.1.6 + update_version_files.py --viewer --version=1.18.1.6 # Update just the server build number to the current SVN revision: update_version_files.py --server - + # Update the viewer channel update_version_files.py --channel="First Look Puppeteering" - + # Update the server channel update_version_files.py --server_channel="Het Grid" - + """ def _getstatusoutput(cmd): """Return Win32 (status, output) of executing cmd @@ -304,4 +304,3 @@ def main(): return 0 main() - From 5b8b88e05af69de4130562d89b7212f1545b450d Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 17:56:25 -0400 Subject: [PATCH 119/123] Nuked ancient uneeded things --- indra/llimage/CMakeLists.txt | 4 - indra/llimage/aes.cpp | 1380 ----------------------- indra/llimage/aes.h | 190 ---- indra/llimage/llimagemetadatareader.cpp | 213 ---- indra/llimage/llimagemetadatareader.h | 32 - indra/llui/lllineeditor.cpp | 1 - indra/newview/llvoavatar.cpp | 3 - 7 files changed, 1823 deletions(-) delete mode 100644 indra/llimage/aes.cpp delete mode 100644 indra/llimage/aes.h delete mode 100644 indra/llimage/llimagemetadatareader.cpp delete mode 100644 indra/llimage/llimagemetadatareader.h diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 97989030a..b292fad2a 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -22,13 +22,11 @@ include_directories( ) set(llimage_SOURCE_FILES - aes.cpp llimagebmp.cpp llimage.cpp llimagedxt.cpp llimagej2c.cpp llimagejpeg.cpp - llimagemetadatareader.cpp llimagepng.cpp llimagetga.cpp llimageworker.cpp @@ -37,13 +35,11 @@ set(llimage_SOURCE_FILES set(llimage_HEADER_FILES CMakeLists.txt - aes.h llimage.h llimagebmp.h llimagedxt.h llimagej2c.h llimagejpeg.h - llimagemetadatareader.h llimagepng.h llimagetga.h llimageworker.h diff --git a/indra/llimage/aes.cpp b/indra/llimage/aes.cpp deleted file mode 100644 index 2dc45ea35..000000000 --- a/indra/llimage/aes.cpp +++ /dev/null @@ -1,1380 +0,0 @@ - -//Rijndael.cpp - -#include -#include -#include -#include "aes.h" - -const int CRijndael::sm_alog[256] = -{ - 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, - 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, - 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, - 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, - 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, - 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, - 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, - 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, - 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, - 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, - 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, - 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, - 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, - 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, - 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, - 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1 -}; - -const int CRijndael::sm_log[256] = -{ - 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, - 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, - 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120, - 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142, - 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, - 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, - 126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186, - 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87, - 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, - 44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, - 127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, - 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, - 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, - 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, - 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, - 103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7 -}; - -const char CRijndael::sm_S[256] = -{ - 99, 124, 119, 123, -14, 107, 111, -59, 48, 1, 103, 43, -2, -41, -85, 118, - -54, -126, -55, 125, -6, 89, 71, -16, -83, -44, -94, -81, -100, -92, 114, -64, - -73, -3, -109, 38, 54, 63, -9, -52, 52, -91, -27, -15, 113, -40, 49, 21, - 4, -57, 35, -61, 24, -106, 5, -102, 7, 18, -128, -30, -21, 39, -78, 117, - 9, -125, 44, 26, 27, 110, 90, -96, 82, 59, -42, -77, 41, -29, 47, -124, - 83, -47, 0, -19, 32, -4, -79, 91, 106, -53, -66, 57, 74, 76, 88, -49, - -48, -17, -86, -5, 67, 77, 51, -123, 69, -7, 2, 127, 80, 60, -97, -88, - 81, -93, 64, -113, -110, -99, 56, -11, -68, -74, -38, 33, 16, -1, -13, -46, - -51, 12, 19, -20, 95, -105, 68, 23, -60, -89, 126, 61, 100, 93, 25, 115, - 96, -127, 79, -36, 34, 42, -112, -120, 70, -18, -72, 20, -34, 94, 11, -37, - -32, 50, 58, 10, 73, 6, 36, 92, -62, -45, -84, 98, -111, -107, -28, 121, - -25, -56, 55, 109, -115, -43, 78, -87, 108, 86, -12, -22, 101, 122, -82, 8, - -70, 120, 37, 46, 28, -90, -76, -58, -24, -35, 116, 31, 75, -67, -117, -118, - 112, 62, -75, 102, 72, 3, -10, 14, 97, 53, 87, -71, -122, -63, 29, -98, - -31, -8, -104, 17, 105, -39, -114, -108, -101, 30, -121, -23, -50, 85, 40, -33, - -116, -95, -119, 13, -65, -26, 66, 104, 65, -103, 45, 15, -80, 84, -69, 22 -}; - -const char CRijndael::sm_Si[256] = -{ - 82, 9, 106, -43, 48, 54, -91, 56, -65, 64, -93, -98, -127, -13, -41, -5, - 124, -29, 57, -126, -101, 47, -1, -121, 52, -114, 67, 68, -60, -34, -23, -53, - 84, 123, -108, 50, -90, -62, 35, 61, -18, 76, -107, 11, 66, -6, -61, 78, - 8, 46, -95, 102, 40, -39, 36, -78, 118, 91, -94, 73, 109, -117, -47, 37, - 114, -8, -10, 100, -122, 104, -104, 22, -44, -92, 92, -52, 93, 101, -74, -110, - 108, 112, 72, 80, -3, -19, -71, -38, 94, 21, 70, 87, -89, -115, -99, -124, - -112, -40, -85, 0, -116, -68, -45, 10, -9, -28, 88, 5, -72, -77, 69, 6, - -48, 44, 30, -113, -54, 63, 15, 2, -63, -81, -67, 3, 1, 19, -118, 107, - 58, -111, 17, 65, 79, 103, -36, -22, -105, -14, -49, -50, -16, -76, -26, 115, - -106, -84, 116, 34, -25, -83, 53, -123, -30, -7, 55, -24, 28, 117, -33, 110, - 71, -15, 26, 113, 29, 41, -59, -119, 111, -73, 98, 14, -86, 24, -66, 27, - -4, 86, 62, 75, -58, -46, 121, 32, -102, -37, -64, -2, 120, -51, 90, -12, - 31, -35, -88, 51, -120, 7, -57, 49, -79, 18, 16, 89, 39, -128, -20, 95, - 96, 81, 127, -87, 25, -75, 74, 13, 45, -27, 122, -97, -109, -55, -100, -17, - -96, -32, 59, 77, -82, 42, -11, -80, -56, -21, -69, 60, -125, 83, -103, 97, - 23, 43, 4, 126, -70, 119, -42, 38, -31, 105, 20, 99, 85, 33, 12, 125 -}; - -const int CRijndael::sm_T1[256] = -{ - -966564955, -126059388, -294160487, -159679603, - -855539, -697603139, -563122255, -1849309868, - 1613770832, 33620227, -832084055, 1445669757, - -402719207, -1244145822, 1303096294, -327780710, - -1882535355, 528646813, -1983264448, -92439161, - -268764651, -1302767125, -1907931191, -68095989, - 1101901292, -1277897625, 1604494077, 1169141738, - 597466303, 1403299063, -462261610, -1681866661, - 1974974402, -503448292, 1033081774, 1277568618, - 1815492186, 2118074177, -168298750, -2083730353, - 1748251740, 1369810420, -773462732, -101584632, - -495881837, -1411852173, 1647391059, 706024767, - 134480908, -1782069422, 1176707941, -1648114850, - 806885416, 932615841, 168101135, 798661301, - 235341577, 605164086, 461406363, -538779075, - -840176858, 1311188841, 2142417613, -361400929, - 302582043, 495158174, 1479289972, 874125870, - 907746093, -596742478, -1269146898, 1537253627, - -1538108682, 1983593293, -1210657183, 2108928974, - 1378429307, -572267714, 1580150641, 327451799, - -1504488459, -1177431704, 0, -1041371860, - 1075847264, -469959649, 2041688520, -1235526675, - -731223362, -1916023994, 1740553945, 1916352843, - -1807070498, -1739830060, -1336387352, -2049978550, - -1143943061, -974131414, 1336584933, -302253290, - -2042412091, -1706209833, 1714631509, 293963156, - -1975171633, -369493744, 67240454, -25198719, - -1605349136, 2017213508, 631218106, 1269344483, - -1571728909, 1571005438, -2143272768, 93294474, - 1066570413, 563977660, 1882732616, -235539196, - 1673313503, 2008463041, -1344611723, 1109467491, - 537923632, -436207846, -34344178, -1076702611, - -2117218996, 403442708, 638784309, -1007883217, - -1101045791, 899127202, -2008791860, 773265209, - -1815821225, 1437050866, -58818942, 2050833735, - -932944724, -1168286233, 840505643, -428641387, - -1067425632, 427917720, -1638969391, -1545806721, - 1143087718, 1412049534, 999329963, 193497219, - -1941551414, -940642775, 1807268051, 672404540, - -1478566279, -1134666014, 369822493, -1378100362, - -606019525, 1681011286, 1949973070, 336202270, - -1840690725, 201721354, 1210328172, -1201906460, - -1614626211, -1110191250, 1135389935, -1000185178, - 965841320, 831886756, -739974089, -226920053, - -706222286, -1949775805, 1849112409, -630362697, - 26054028, -1311386268, -1672589614, 1235855840, - -663982924, -1403627782, -202050553, -806688219, - -899324497, -193299826, 1202630377, 268961816, - 1874508501, -260540280, 1243948399, 1546530418, - 941366308, 1470539505, 1941222599, -1748580783, - -873928669, -1579295364, -395021156, 1042226977, - -1773450275, 1639824860, 227249030, 260737669, - -529502064, 2084453954, 1907733956, -865704278, - -1874310952, 100860677, -134810111, 470683154, - -1033805405, 1781871967, -1370007559, 1773779408, - 394692241, -1715355304, 974986535, 664706745, - -639508168, -336005101, 731420851, 571543859, - -764843589, -1445340816, 126783113, 865375399, - 765172662, 1008606754, 361203602, -907417312, - -2016489911, -1437248001, 1344809080, -1512054918, - 59542671, 1503764984, 160008576, 437062935, - 1707065306, -672733647, -2076032314, -798463816, - -2109652541, 697932208, 1512910199, 504303377, - 2075177163, -1470868228, 1841019862, 739644986 -}; - -const int CRijndael::sm_T2[256] = -{ - -1513725085, -2064089988, -1712425097, -1913226373, - 234877682, -1110021269, -1310822545, 1418839493, - 1348481072, 50462977, -1446090905, 2102799147, - 434634494, 1656084439, -431117397, -1695779210, - 1167051466, -1658879358, 1082771913, -2013627011, - 368048890, -340633255, -913422521, 201060592, - -331240019, 1739838676, -44064094, -364531793, - -1088185188, -145513308, -1763413390, 1536934080, - -1032472649, 484572669, -1371696237, 1783375398, - 1517041206, 1098792767, 49674231, 1334037708, - 1550332980, -195975771, 886171109, 150598129, - -1813876367, 1940642008, 1398944049, 1059722517, - 201851908, 1385547719, 1699095331, 1587397571, - 674240536, -1590192490, 252314885, -1255171430, - 151914247, 908333586, -1692696448, 1038082786, - 651029483, 1766729511, -847269198, -1612024459, - 454166793, -1642232957, 1951935532, 775166490, - 758520603, -1294176658, -290170278, -77881184, - -157003182, 1299594043, 1639438038, -830622797, - 2068982057, 1054729187, 1901997871, -1760328572, - -173649069, 1757008337, 0, 750906861, - 1614815264, 535035132, -931548751, -306816165, - -1093375382, 1183697867, -647512386, 1265776953, - -560706998, -728216500, -391096232, 1250283471, - 1807470800, 717615087, -447763798, 384695291, - -981056701, -677753523, 1432761139, -1810791035, - -813021883, 283769337, 100925954, -2114027649, - -257929136, 1148730428, -1171939425, -481580888, - -207466159, -27417693, -1065336768, -1979347057, - -1388342638, -1138647651, 1215313976, 82966005, - -547111748, -1049119050, 1974459098, 1665278241, - 807407632, 451280895, 251524083, 1841287890, - 1283575245, 337120268, 891687699, 801369324, - -507617441, -1573546089, -863484860, 959321879, - 1469301956, -229267545, -2097381762, 1199193405, - -1396153244, -407216803, 724703513, -1780059277, - -1598005152, -1743158911, -778154161, 2141445340, - 1715741218, 2119445034, -1422159728, -2096396152, - -896776634, 700968686, -747915080, 1009259540, - 2041044702, -490971554, 487983883, 1991105499, - 1004265696, 1449407026, 1316239930, 504629770, - -611169975, 168560134, 1816667172, -457679780, - 1570751170, 1857934291, -280777556, -1497079198, - -1472622191, -1540254315, 936633572, -1947043463, - 852879335, 1133234376, 1500395319, -1210421907, - -1946055283, 1689376213, -761508274, -532043351, - -1260884884, -89369002, 133428468, 634383082, - -1345690267, -1896580486, -381178194, 403703816, - -714097990, -1997506440, 1867130149, 1918643758, - 607656988, -245913946, -948718412, 1368901318, - 600565992, 2090982877, -1662487436, 557719327, - -577352885, -597574211, -2045932661, -2062579062, - -1864339344, 1115438654, -999180875, -1429445018, - -661632952, 84280067, 33027830, 303828494, - -1547542175, 1600795957, -106014889, -798377543, - -1860729210, 1486471617, 658119965, -1188585826, - 953803233, 334231800, -1288988520, 857870609, - -1143838359, 1890179545, -1995993458, -1489791852, - -1238525029, 574365214, -1844082809, 550103529, - 1233637070, -5614251, 2018519080, 2057691103, - -1895592820, -128343647, -2146858615, 387583245, - -630865985, 836232934, -964410814, -1194301336, - -1014873791, -1339450983, 2002398509, 287182607, - -881086288, -56077228, -697451589, 975967766 -}; - -const int CRijndael::sm_T3[256] = -{ - 1671808611, 2089089148, 2006576759, 2072901243, - -233963534, 1807603307, 1873927791, -984313403, - 810573872, 16974337, 1739181671, 729634347, - -31856642, -681396777, -1410970197, 1989864566, - -901410870, -2103631998, -918517303, 2106063485, - -99225606, 1508618841, 1204391495, -267650064, - -1377025619, -731401260, -1560453214, -1343601233, - -1665195108, -1527295068, 1922491506, -1067738176, - -1211992649, -48438787, -1817297517, 644500518, - 911895606, 1061256767, -150800905, -867204148, - 878471220, -1510714971, -449523227, -251069967, - 1905517169, -663508008, 827548209, 356461077, - 67897348, -950889017, 593839651, -1017209405, - 405286936, -1767819370, 84871685, -1699401830, - 118033927, 305538066, -2137318528, -499261470, - -349778453, 661212711, -1295155278, 1973414517, - 152769033, -2086789757, 745822252, 439235610, - 455947803, 1857215598, 1525593178, -1594139744, - 1391895634, 994932283, -698239018, -1278313037, - 695947817, -482419229, 795958831, -2070473852, - 1408607827, -781665839, 0, -315833875, - 543178784, -65018884, -1312261711, 1542305371, - 1790891114, -884568629, -1093048386, 961245753, - 1256100938, 1289001036, 1491644504, -817199665, - -798245936, -282409489, -1427812438, -82383365, - 1137018435, 1305975373, 861234739, -2053893755, - 1171229253, -116332039, 33948674, 2139225727, - 1357946960, 1011120188, -1615190625, -1461498968, - 1374921297, -1543610973, 1086357568, -1886780017, - -1834139758, -1648615011, 944271416, -184225291, - -1126210628, -1228834890, -629821478, 560153121, - 271589392, -15014401, -217121293, -764559406, - -850624051, 202643468, 322250259, -332413972, - 1608629855, -1750977129, 1154254916, 389623319, - -1000893500, -1477290585, 2122513534, 1028094525, - 1689045092, 1575467613, 422261273, 1939203699, - 1621147744, -2120738431, 1339137615, -595614756, - 577127458, 712922154, -1867826288, -2004677752, - 1187679302, -299251730, -1194103880, 339486740, - -562452514, 1591917662, 186455563, -612979237, - -532948000, 844522546, 978220090, 169743370, - 1239126601, 101321734, 611076132, 1558493276, - -1034051646, -747717165, -1393605716, 1655096418, - -1851246191, -1784401515, -466103324, 2039214713, - -416098841, -935097400, 928607799, 1840765549, - -1920204403, -714821163, 1322425422, -1444918871, - 1823791212, 1459268694, -200805388, -366620694, - 1706019429, 2056189050, -1360443474, 135794696, - -1160417350, 2022240376, 628050469, 779246638, - 472135708, -1494132826, -1261997132, -967731258, - -400307224, -579034659, 1956440180, 522272287, - 1272813131, -1109630531, -1954148981, -1970991222, - 1888542832, 1044544574, -1245417035, 1722469478, - 1222152264, 50660867, -167643146, 236067854, - 1638122081, 895445557, 1475980887, -1177523783, - -2037311610, -1051158079, 489110045, -1632032866, - -516367903, -132912136, -1733088360, 288563729, - 1773916777, -646927911, -1903622258, -1800981612, - -1682559589, 505560094, -2020469369, -383727127, - -834041906, 1442818645, 678973480, -545610273, - -1936784500, -1577559647, -1988097655, 219617805, - -1076206145, -432941082, 1120306242, 1756942440, - 1103331905, -1716508263, 762796589, 252780047, - -1328841808, 1425844308, -1143575109, 372911126 -}; - -const int CRijndael::sm_T4[256] = -{ - 1667474886, 2088535288, 2004326894, 2071694838, - -219017729, 1802223062, 1869591006, -976923503, - 808472672, 16843522, 1734846926, 724270422, - -16901657, -673750347, -1414797747, 1987484396, - -892713585, -2105369313, -909557623, 2105378810, - -84273681, 1499065266, 1195886990, -252703749, - -1381110719, -724277325, -1566376609, -1347425723, - -1667449053, -1532692653, 1920112356, -1061135461, - -1212693899, -33743647, -1819038147, 640051788, - 909531756, 1061110142, -134806795, -859025533, - 875846760, -1515850671, -437963567, -235861767, - 1903268834, -656903253, 825316194, 353713962, - 67374088, -943238507, 589522246, -1010606435, - 404236336, -1768513225, 84217610, -1701137105, - 117901582, 303183396, -2139055333, -488489505, - -336910643, 656894286, -1296904833, 1970642922, - 151591698, -2088526307, 741110872, 437923380, - 454765878, 1852748508, 1515908788, -1600062629, - 1381168804, 993742198, -690593353, -1280061827, - 690584402, -471646499, 791638366, -2071685357, - 1398011302, -774805319, 0, -303223615, - 538992704, -50585629, -1313748871, 1532751286, - 1785380564, -875870579, -1094788761, 960056178, - 1246420628, 1280103576, 1482221744, -808498555, - -791647301, -269538619, -1431640753, -67430675, - 1128514950, 1296947098, 859002214, -2054843375, - 1162203018, -101117719, 33687044, 2139062782, - 1347481760, 1010582648, -1616922075, -1465326773, - 1364325282, -1549533603, 1077985408, -1886418427, - -1835881153, -1650607071, 943212656, -168491791, - -1128472733, -1229536905, -623217233, 555836226, - 269496352, -58651, -202174723, -757961281, - -842183551, 202118168, 320025894, -320065597, - 1600119230, -1751670219, 1145359496, 387397934, - -993765485, -1482165675, 2122220284, 1027426170, - 1684319432, 1566435258, 421079858, 1936954854, - 1616945344, -2122213351, 1330631070, -589529181, - 572679748, 707427924, -1869567173, -2004319477, - 1179044492, -286381625, -1195846805, 336870440, - -555845209, 1583276732, 185277718, -606374227, - -522175525, 842159716, 976899700, 168435220, - 1229577106, 101059084, 606366792, 1549591736, - -1027449441, -741118275, -1397952701, 1650632388, - -1852725191, -1785355215, -454805549, 2038008818, - -404278571, -926399605, 926374254, 1835907034, - -1920103423, -707435343, 1313788572, -1448484791, - 1819063512, 1448540844, -185333773, -353753649, - 1701162954, 2054852340, -1364268729, 134748176, - -1162160785, 2021165296, 623210314, 774795868, - 471606328, -1499008681, -1263220877, -960081513, - -387439669, -572687199, 1953799400, 522133822, - 1263263126, -1111630751, -1953790451, -1970633457, - 1886425312, 1044267644, -1246378895, 1718004428, - 1212733584, 50529542, -151649801, 235803164, - 1633788866, 892690282, 1465383342, -1179004823, - -2038001385, -1044293479, 488449850, -1633765081, - -505333543, -117959701, -1734823125, 286339874, - 1768537042, -640061271, -1903261433, -1802197197, - -1684294099, 505291324, -2021158379, -370597687, - -825341561, 1431699370, 673740880, -539002203, - -1936945405, -1583220647, -1987477495, 218961690, - -1077945755, -421121577, 1111672452, 1751693520, - 1094828930, -1717981143, 757954394, 252645662, - -1330590853, 1414855848, -1145317779, 370555436 -}; - -const int CRijndael::sm_T5[256] = -{ - 1374988112, 2118214995, 437757123, 975658646, - 1001089995, 530400753, -1392879445, 1273168787, - 540080725, -1384747530, -1999866223, -184398811, - 1340463100, -987051049, 641025152, -1251826801, - -558802359, 632953703, 1172967064, 1576976609, - -1020300030, -2125664238, -1924753501, 1809054150, - 59727847, 361929877, -1083344149, -1789765158, - -725712083, 1484005843, 1239443753, -1899378620, - 1975683434, -191989384, -1722270101, 666464733, - -1092530250, -259478249, -920605594, 2110667444, - 1675577880, -451268222, -1756286112, 1649639237, - -1318815776, -1150570876, -25059300, -116905068, - 1883793496, -1891238631, -1797362553, 1383856311, - -1418472669, 1917518562, -484470953, 1716890410, - -1293211641, 800440835, -2033878118, -751368027, - 807962610, 599762354, 33778362, -317291940, - -1966138325, -1485196142, -217582864, 1315562145, - 1708848333, 101039829, -785096161, -995688822, - 875451293, -1561111136, 92987698, -1527321739, - 193195065, 1080094634, 1584504582, -1116860335, - 1042385657, -1763899843, -583137874, 1306967366, - -1856729675, 1908694277, 67556463, 1615861247, - 429456164, -692196969, -1992277044, 1742315127, - -1326955843, 126454664, -417768648, 2043211483, - -1585706425, 2084704233, -125559095, 0, - 159417987, 841739592, 504459436, 1817866830, - -49348613, 260388950, 1034867998, 908933415, - 168810852, 1750902305, -1688513327, 607530554, - 202008497, -1822955761, -1259432238, 463180190, - -2134850225, 1641816226, 1517767529, 470948374, - -493635062, -1063245083, 1008918595, 303765277, - 235474187, -225720403, 766945465, 337553864, - 1475418501, -1351284916, -291906117, -1551933187, - -150919521, 1551037884, 1147550661, 1543208500, - -1958532746, -886847780, -1225917336, -1192955549, - -684598070, 1113818384, 328671808, -2067394272, - -2058738563, -759480840, -1359400431, -953573011, - 496906059, -592301837, 226906860, 2009195472, - 733156972, -1452230247, 294930682, 1206477858, - -1459843900, -1594867942, 1451044056, 573804783, - -2025238841, -650587711, -1932877058, -1730933962, - -1493859889, -1518674392, -625504730, 1068351396, - 742039012, 1350078989, 1784663195, 1417561698, - -158526526, -1864845080, 775550814, -2101104651, - -1621262146, 1775276924, 1876241833, -819653965, - -928212677, 270040487, -392404114, -616842373, - -853116919, 1851332852, -325404927, -2091935064, - -426414491, -1426069890, 566021896, -283776794, - -1159226407, 1248802510, -358676012, 699432150, - 832877231, 708780849, -962227152, 899835584, - 1951317047, -58537306, -527380304, 866637845, - -251357110, 1106041591, 2144161806, 395441711, - 1984812685, 1139781709, -861254316, -459930401, - -1630423581, 1282050075, -1054072904, 1181045119, - -1654724092, 25965917, -91786125, -83148498, - -1285087910, -1831087534, -384805325, 1842759443, - -1697160820, 933301370, 1509430414, -351060855, - -827774994, -1218328267, -518199827, 2051518780, - -1663901863, 1441952575, 404016761, 1942435775, - 1408749034, 1610459739, -549621996, 2017778566, - -894438527, -1184316354, 941896748, -1029488545, - 371049330, -1126030068, 675039627, -15887039, - 967311729, 135050206, -659233636, 1683407248, - 2076935265, -718096784, 1215061108, -793225406 -}; - -const int CRijndael::sm_T6[256] = -{ - 1347548327, 1400783205, -1021700188, -1774573730, - -885281941, -249586363, -1414727080, -1823743229, - 1428173050, -156404115, -1853305738, 636813900, - -61872681, -674944309, -2144979644, -1883938141, - 1239331162, 1730525723, -1740248562, -513933632, - 46346101, 310463728, -1551022441, -966011911, - -419197089, -1793748324, -339776134, -627748263, - 768917123, -749177823, 692707433, 1150208456, - 1786102409, 2029293177, 1805211710, -584599183, - -1229004465, 401639597, 1724457132, -1266823622, - 409198410, -2098914767, 1620529459, 1164071807, - -525245321, -2068091986, 486441376, -1795618773, - 1483753576, 428819965, -2020286868, -1219331080, - 598438867, -495826174, 1474502543, 711349675, - 129166120, 53458370, -1702443653, -1512884472, - -231724921, -1306280027, -1174273174, 1559041666, - 730517276, -1834518092, -252508174, -1588696606, - -848962828, -721025602, 533804130, -1966823682, - -1657524653, -1599933611, 839224033, 1973745387, - 957055980, -1438621457, 106852767, 1371368976, - -113368694, 1033297158, -1361232379, 1179510461, - -1248766835, 91341917, 1862534868, -10465259, - 605657339, -1747534359, -863420349, 2003294622, - -1112479678, -2012771957, 954669403, -612775698, - 1201765386, -377732593, -906460130, 0, - -2096529274, 1211247597, -1407315600, 1315723890, - -67301633, 1443857720, 507358933, 657861945, - 1678381017, 560487590, -778347692, 975451694, - -1324610969, 261314535, -759894378, -1642357871, - 1333838021, -1570644960, 1767536459, 370938394, - 182621114, -440360918, 1128014560, 487725847, - 185469197, -1376613433, -1188186456, -938205527, - -2057834215, 1286567175, -1141990947, -39616672, - -1611202266, -1134791947, -985373125, 878443390, - 1988838185, -590666810, 1756818940, 1673061617, - -891866660, 272786309, 1075025698, 545572369, - 2105887268, -120407235, 296679730, 1841768865, - 1260232239, -203640272, -334657966, -797457949, - 1814803222, -1716948807, -99511224, 575138148, - -995558260, 446754879, -665420500, -282971248, - -947435186, -1042728751, -24327518, 915985419, - -811141759, 681933534, 651868046, -1539330625, - -466863459, 223377554, -1687527476, 1649704518, - -1024029421, -393160520, 1580087799, -175979601, - -1096852096, 2087309459, -1452288723, -1278270190, - 1003007129, -1492117379, 1860738147, 2077965243, - 164439672, -194094824, 32283319, -1467789414, - 1709610350, 2125135846, 136428751, -420538904, - -642062437, -833982666, -722821367, -701910916, - -1355701070, 824852259, 818324884, -1070226842, - 930369212, -1493400886, -1327460144, 355706840, - 1257309336, -146674470, 243256656, 790073846, - -1921626666, 1296297904, 1422699085, -538667516, - -476130891, 457992840, -1195299809, 2135319889, - 77422314, 1560382517, 1945798516, 788204353, - 1521706781, 1385356242, 870912086, 325965383, - -1936009375, 2050466060, -1906706412, -1981082820, - -288446169, 901210569, -304014107, 1014646705, - 1503449823, 1062597235, 2031621326, -1082931401, - -363595827, 1533017514, 350174575, -2038938405, - -2117423117, 1052338372, 741876788, 1606591296, - 1914052035, 213705253, -1960297399, 1107234197, - 1899603969, -569897805, -1663519516, -1872472383, - 1635502980, 1893020342, 1950903388, 1120974935 -}; - -const int CRijndael::sm_T7[256] = -{ - -1487908364, 1699970625, -1530717673, 1586903591, - 1808481195, 1173430173, 1487645946, 59984867, - -95084496, 1844882806, 1989249228, 1277555970, - -671330331, -875051734, 1149249077, -1550863006, - 1514790577, 459744698, 244860394, -1058972162, - 1963115311, -267222708, -1750889146, -104436781, - 1608975247, -1667951214, 2062270317, 1507497298, - -2094148418, 567498868, 1764313568, -935031095, - -1989511742, 2037970062, 1047239000, 1910319033, - 1337376481, -1390940024, -1402549984, 984907214, - 1243112415, 830661914, 861968209, 2135253587, - 2011214180, -1367032981, -1608712575, 731183368, - 1750626376, -48656571, 1820824798, -122203525, - -752637069, 48394827, -1890065633, -1423284651, - 671593195, -1039978571, 2073724613, 145085239, - -2014171096, -1515052097, 1790575107, -2107839210, - 472615631, -1265457287, -219090169, -492745111, - -187865638, -1093335547, 1646252340, -24460122, - 1402811438, 1436590835, -516815478, -344611594, - -331805821, -274055072, -1626972559, 273792366, - -1963377119, 104699613, 95345982, -1119466010, - -1917480620, 1560637892, -730921978, 369057872, - -81520232, -375925059, 1137477952, -1636341799, - 1119727848, -1954019447, 1530455833, -287606328, - 172466556, 266959938, 516552836, 0, - -2038232704, -314035669, 1890328081, 1917742170, - -262898, 945164165, -719438418, 958871085, - -647755249, -1507760036, 1423022939, 775562294, - 1739656202, -418409641, -1764576018, -1851909221, - -984645440, 547512796, 1265195639, 437656594, - -1173691757, 719700128, -532464606, 387781147, - 218828297, -944901493, -1464259146, -1446505442, - 428169201, 122466165, -574886247, 1627235199, - 648017665, -172204942, 1002783846, 2117360635, - 695634755, -958608605, -60246291, -245122844, - -590686415, -2062531997, 574624663, 287343814, - 612205898, 1039717051, 840019705, -1586641111, - 793451934, 821288114, 1391201670, -472877119, - 376187827, -1181111952, 1224348052, 1679968233, - -1933268740, 1058709744, 752375421, -1863376333, - 1321699145, -775825096, -1560376118, 188127444, - -2117097739, -567761542, -1910056265, -1079754835, - -1645990854, -1844621192, -862229921, 1180849278, - 331544205, -1192718120, -144822727, -1342864701, - -2134991011, -1820562992, 766078933, 313773861, - -1724135252, 2108100632, 1668212892, -1149510853, - 2013908262, 418672217, -1224610662, -1700232369, - 1852171925, -427906305, -821550660, -387518699, - -1680229657, 919489135, 164948639, 2094410160, - -1297141340, 590424639, -1808742747, 1723872674, - -1137216434, -895026046, -793714544, -669699161, - -1739919100, -621329940, 1343127501, -164685935, - -695372211, -1337113617, 1297403050, 81781910, - -1243373871, -2011476886, 532201772, 1367295589, - -368796322, 895287692, 1953757831, 1093597963, - 492483431, -766340389, 1446242576, 1192455638, - 1636604631, 209336225, 344873464, 1015671571, - 669961897, -919226527, -437395172, -1321436601, - -547775278, 1933530610, -830924780, 935293895, - -840281097, -1436852227, 1863638845, -611944380, - -209597777, -1002522264, 875313188, 1080017571, - -1015933411, 621591778, 1233856572, -1790836979, - 24197544, -1277294580, -459482956, -1047501738, - -2073986101, -1234119374, 1551124588, 1463996600 -}; - -const int CRijndael::sm_T8[256] = -{ - -190361519, 1097159550, 396673818, 660510266, - -1418998981, -1656360673, -94852180, -486304949, - 821712160, 1986918061, -864644728, 38544885, - -438830001, 718002117, 893681702, 1654886325, - -1319482914, -1172609243, -368142267, -20913827, - 796197571, 1290801793, 1184342925, -738605461, - -1889540349, -1835231979, 1836772287, 1381620373, - -1098699308, 1948373848, -529979063, -909622130, - -1031181707, -1904641804, 1480485785, -1183720153, - -514869570, -2001922064, 548169417, -835013507, - -548792221, 439452389, 1362321559, 1400849762, - 1685577905, 1806599355, -2120213250, 137073913, - 1214797936, 1174215055, -563312748, 2079897426, - 1943217067, 1258480242, 529487843, 1437280870, - -349698126, -1245576401, -981755258, 923313619, - 679998000, -1079659997, 57326082, 377642221, - -820237430, 2041877159, 133361907, 1776460110, - -621490843, 96392454, 878845905, -1493267772, - 777231668, -212492126, -1964953083, -152341084, - -2081670901, 1626319424, 1906247262, 1846563261, - 562755902, -586793578, 1040559837, -423803315, - 1418573201, -1000536719, 114585348, 1343618912, - -1728371687, -1108764714, 1078185097, -643926169, - -398279248, -1987344377, 425408743, -923870343, - 2081048481, 1108339068, -2078357000, 0, - -2138668279, 736970802, 292596766, 1517440620, - 251657213, -2059905521, -1361764803, 758720310, - 265905162, 1554391400, 1532285339, 908999204, - 174567692, 1474760595, -292105548, -1684955621, - -1060810880, -601841055, 2001430874, 303699484, - -1816524062, -1607801408, 585122620, 454499602, - 151849742, -1949848078, -1230456531, 514443284, - -249985705, 1963412655, -1713521682, 2137062819, - 19308535, 1928707164, 1715193156, -75615141, - 1126790795, 600235211, -302225226, -453942344, - 836553431, 1669664834, -1759363053, -971956092, - 1243905413, -1153566510, -114159186, 698445255, - -1641067747, -1305414692, -2041385971, -1042034569, - -1290376149, 1891211689, -1807156719, -379313593, - -57883480, -264299872, 2100090966, 865136418, - 1229899655, 953270745, -895287668, -737462632, - -176042074, 2061379749, -1215420710, -1379949505, - 983426092, 2022837584, 1607244650, 2118541908, - -1928084746, -658970480, 972512814, -1011878526, - 1568718495, -795640727, -718427793, 621982671, - -1399243832, 410887952, -1671205144, 1002142683, - 645401037, 1494807662, -1699282452, 1335535747, - -1787927066, -1671510, -1127282655, 367585007, - -409216582, 1865862730, -1626745622, -1333995991, - -1531793615, 1059270954, -1517014842, -1570324427, - 1320957812, -2100648196, -1865371424, -1479011021, - 77089521, -321194175, -850391425, -1846137065, - 1305906550, -273658557, -1437772596, -1778065436, - -776608866, 1787304780, 740276417, 1699839814, - 1592394909, -1942659839, -2022411270, 188821243, - 1729977011, -606973294, 274084841, -699985043, - -681472870, -1593017801, -132870567, 322734571, - -1457000754, 1640576439, 484830689, 1202797690, - -757114468, -227328171, 349075736, -952647821, - -137500077, -39167137, 1030690015, 1155237496, - -1342996022, 1757691577, 607398968, -1556062270, - 499347990, -500888388, 1011452712, 227885567, - -1476300487, 213114376, -1260086056, 1455525988, - -880516741, 850817237, 1817998408, -1202240816 -}; - -const int CRijndael::sm_U1[256] = -{ - 0, 235474187, 470948374, 303765277, - 941896748, 908933415, 607530554, 708780849, - 1883793496, 2118214995, 1817866830, 1649639237, - 1215061108, 1181045119, 1417561698, 1517767529, - -527380304, -291906117, -58537306, -225720403, - -659233636, -692196969, -995688822, -894438527, - -1864845080, -1630423581, -1932877058, -2101104651, - -1459843900, -1493859889, -1259432238, -1159226407, - -616842373, -718096784, -953573011, -920605594, - -484470953, -317291940, -15887039, -251357110, - -1418472669, -1518674392, -1218328267, -1184316354, - -1822955761, -1654724092, -1891238631, -2125664238, - 1001089995, 899835584, 666464733, 699432150, - 59727847, 226906860, 530400753, 294930682, - 1273168787, 1172967064, 1475418501, 1509430414, - 1942435775, 2110667444, 1876241833, 1641816226, - -1384747530, -1551933187, -1318815776, -1083344149, - -1789765158, -1688513327, -1992277044, -2025238841, - -583137874, -751368027, -1054072904, -819653965, - -451268222, -351060855, -116905068, -150919521, - 1306967366, 1139781709, 1374988112, 1610459739, - 1975683434, 2076935265, 1775276924, 1742315127, - 1034867998, 866637845, 566021896, 800440835, - 92987698, 193195065, 429456164, 395441711, - 1984812685, 2017778566, 1784663195, 1683407248, - 1315562145, 1080094634, 1383856311, 1551037884, - 101039829, 135050206, 437757123, 337553864, - 1042385657, 807962610, 573804783, 742039012, - -1763899843, -1730933962, -1966138325, -2067394272, - -1359400431, -1594867942, -1293211641, -1126030068, - -426414491, -392404114, -91786125, -191989384, - -558802359, -793225406, -1029488545, -861254316, - 1106041591, 1340463100, 1576976609, 1408749034, - 2043211483, 2009195472, 1708848333, 1809054150, - 832877231, 1068351396, 766945465, 599762354, - 159417987, 126454664, 361929877, 463180190, - -1585706425, -1351284916, -1116860335, -1285087910, - -1722270101, -1756286112, -2058738563, -1958532746, - -785096161, -549621996, -853116919, -1020300030, - -384805325, -417768648, -184398811, -83148498, - -1697160820, -1797362553, -2033878118, -1999866223, - -1561111136, -1392879445, -1092530250, -1326955843, - -358676012, -459930401, -158526526, -125559095, - -759480840, -592301837, -827774994, -1063245083, - 2051518780, 1951317047, 1716890410, 1750902305, - 1113818384, 1282050075, 1584504582, 1350078989, - 168810852, 67556463, 371049330, 404016761, - 841739592, 1008918595, 775550814, 540080725, - -325404927, -493635062, -259478249, -25059300, - -725712083, -625504730, -928212677, -962227152, - -1663901863, -1831087534, -2134850225, -1899378620, - -1527321739, -1426069890, -1192955549, -1225917336, - 202008497, 33778362, 270040487, 504459436, - 875451293, 975658646, 675039627, 641025152, - 2084704233, 1917518562, 1615861247, 1851332852, - 1147550661, 1248802510, 1484005843, 1451044056, - 933301370, 967311729, 733156972, 632953703, - 260388950, 25965917, 328671808, 496906059, - 1206477858, 1239443753, 1543208500, 1441952575, - 2144161806, 1908694277, 1675577880, 1842759443, - -684598070, -650587711, -886847780, -987051049, - -283776794, -518199827, -217582864, -49348613, - -1485196142, -1452230247, -1150570876, -1251826801, - -1621262146, -1856729675, -2091935064, -1924753501 -}; - -const int CRijndael::sm_U2[256] = -{ - 0, 185469197, 370938394, 487725847, - 741876788, 657861945, 975451694, 824852259, - 1483753576, 1400783205, 1315723890, 1164071807, - 1950903388, 2135319889, 1649704518, 1767536459, - -1327460144, -1141990947, -1493400886, -1376613433, - -1663519516, -1747534359, -1966823682, -2117423117, - -393160520, -476130891, -24327518, -175979601, - -995558260, -811141759, -759894378, -642062437, - 2077965243, 1893020342, 1841768865, 1724457132, - 1474502543, 1559041666, 1107234197, 1257309336, - 598438867, 681933534, 901210569, 1052338372, - 261314535, 77422314, 428819965, 310463728, - -885281941, -1070226842, -584599183, -701910916, - -419197089, -334657966, -249586363, -99511224, - -1823743229, -1740248562, -2057834215, -1906706412, - -1082931401, -1266823622, -1452288723, -1570644960, - -156404115, -39616672, -525245321, -339776134, - -627748263, -778347692, -863420349, -947435186, - -1361232379, -1512884472, -1195299809, -1278270190, - -2098914767, -1981082820, -1795618773, -1611202266, - 1179510461, 1296297904, 1347548327, 1533017514, - 1786102409, 1635502980, 2087309459, 2003294622, - 507358933, 355706840, 136428751, 53458370, - 839224033, 957055980, 605657339, 790073846, - -1921626666, -2038938405, -1687527476, -1872472383, - -1588696606, -1438621457, -1219331080, -1134791947, - -721025602, -569897805, -1021700188, -938205527, - -113368694, -231724921, -282971248, -466863459, - 1033297158, 915985419, 730517276, 545572369, - 296679730, 446754879, 129166120, 213705253, - 1709610350, 1860738147, 1945798516, 2029293177, - 1239331162, 1120974935, 1606591296, 1422699085, - -146674470, -61872681, -513933632, -363595827, - -612775698, -797457949, -848962828, -966011911, - -1355701070, -1539330625, -1188186456, -1306280027, - -2096529274, -2012771957, -1793748324, -1642357871, - 1201765386, 1286567175, 1371368976, 1521706781, - 1805211710, 1620529459, 2105887268, 1988838185, - 533804130, 350174575, 164439672, 46346101, - 870912086, 954669403, 636813900, 788204353, - -1936009375, -2020286868, -1702443653, -1853305738, - -1599933611, -1414727080, -1229004465, -1112479678, - -722821367, -538667516, -1024029421, -906460130, - -120407235, -203640272, -288446169, -440360918, - 1014646705, 930369212, 711349675, 560487590, - 272786309, 457992840, 106852767, 223377554, - 1678381017, 1862534868, 1914052035, 2031621326, - 1211247597, 1128014560, 1580087799, 1428173050, - 32283319, 182621114, 401639597, 486441376, - 768917123, 651868046, 1003007129, 818324884, - 1503449823, 1385356242, 1333838021, 1150208456, - 1973745387, 2125135846, 1673061617, 1756818940, - -1324610969, -1174273174, -1492117379, -1407315600, - -1657524653, -1774573730, -1960297399, -2144979644, - -377732593, -495826174, -10465259, -194094824, - -985373125, -833982666, -749177823, -665420500, - 2050466060, 1899603969, 1814803222, 1730525723, - 1443857720, 1560382517, 1075025698, 1260232239, - 575138148, 692707433, 878443390, 1062597235, - 243256656, 91341917, 409198410, 325965383, - -891866660, -1042728751, -590666810, -674944309, - -420538904, -304014107, -252508174, -67301633, - -1834518092, -1716948807, -2068091986, -1883938141, - -1096852096, -1248766835, -1467789414, -1551022441, -}; - -const int CRijndael::sm_U3[256] = -{ - 0, 218828297, 437656594, 387781147, - 875313188, 958871085, 775562294, 590424639, - 1750626376, 1699970625, 1917742170, 2135253587, - 1551124588, 1367295589, 1180849278, 1265195639, - -793714544, -574886247, -895026046, -944901493, - -459482956, -375925059, -24460122, -209597777, - -1192718120, -1243373871, -1560376118, -1342864701, - -1933268740, -2117097739, -1764576018, -1680229657, - -1149510853, -1234119374, -1586641111, -1402549984, - -1890065633, -2107839210, -1790836979, -1739919100, - -752637069, -567761542, -919226527, -1002522264, - -418409641, -368796322, -48656571, -267222708, - 1808481195, 1723872674, 1910319033, 2094410160, - 1608975247, 1391201670, 1173430173, 1224348052, - 59984867, 244860394, 428169201, 344873464, - 935293895, 984907214, 766078933, 547512796, - 1844882806, 1627235199, 2011214180, 2062270317, - 1507497298, 1423022939, 1137477952, 1321699145, - 95345982, 145085239, 532201772, 313773861, - 830661914, 1015671571, 731183368, 648017665, - -1119466010, -1337113617, -1487908364, -1436852227, - -1989511742, -2073986101, -1820562992, -1636341799, - -719438418, -669699161, -821550660, -1039978571, - -516815478, -331805821, -81520232, -164685935, - -695372211, -611944380, -862229921, -1047501738, - -492745111, -274055072, -122203525, -172204942, - -1093335547, -1277294580, -1530717673, -1446505442, - -1963377119, -2014171096, -1863376333, -1645990854, - 104699613, 188127444, 472615631, 287343814, - 840019705, 1058709744, 671593195, 621591778, - 1852171925, 1668212892, 1953757831, 2037970062, - 1514790577, 1463996600, 1080017571, 1297403050, - -621329940, -671330331, -1058972162, -840281097, - -287606328, -472877119, -187865638, -104436781, - -1297141340, -1079754835, -1464259146, -1515052097, - -2038232704, -1954019447, -1667951214, -1851909221, - 172466556, 122466165, 273792366, 492483431, - 1047239000, 861968209, 612205898, 695634755, - 1646252340, 1863638845, 2013908262, 1963115311, - 1446242576, 1530455833, 1277555970, 1093597963, - 1636604631, 1820824798, 2073724613, 1989249228, - 1436590835, 1487645946, 1337376481, 1119727848, - 164948639, 81781910, 331544205, 516552836, - 1039717051, 821288114, 669961897, 719700128, - -1321436601, -1137216434, -1423284651, -1507760036, - -2062531997, -2011476886, -1626972559, -1844621192, - -647755249, -730921978, -1015933411, -830924780, - -314035669, -532464606, -144822727, -95084496, - -1224610662, -1173691757, -1390940024, -1608712575, - -2094148418, -1910056265, -1724135252, -1808742747, - -547775278, -766340389, -984645440, -935031095, - -344611594, -427906305, -245122844, -60246291, - 1739656202, 1790575107, 2108100632, 1890328081, - 1402811438, 1586903591, 1233856572, 1149249077, - 266959938, 48394827, 369057872, 418672217, - 1002783846, 919489135, 567498868, 752375421, - 209336225, 24197544, 376187827, 459744698, - 945164165, 895287692, 574624663, 793451934, - 1679968233, 1764313568, 2117360635, 1933530610, - 1343127501, 1560637892, 1243112415, 1192455638, - -590686415, -775825096, -958608605, -875051734, - -387518699, -437395172, -219090169, -262898, - -1265457287, -1181111952, -1367032981, -1550863006, - -2134991011, -1917480620, -1700232369, -1750889146 -}; - -const int CRijndael::sm_U4[256] = -{ - 0, 151849742, 303699484, 454499602, - 607398968, 758720310, 908999204, 1059270954, - 1214797936, 1097159550, 1517440620, 1400849762, - 1817998408, 1699839814, 2118541908, 2001430874, - -1865371424, -1713521682, -2100648196, -1949848078, - -1260086056, -1108764714, -1493267772, -1342996022, - -658970480, -776608866, -895287668, -1011878526, - -57883480, -176042074, -292105548, -409216582, - 1002142683, 850817237, 698445255, 548169417, - 529487843, 377642221, 227885567, 77089521, - 1943217067, 2061379749, 1640576439, 1757691577, - 1474760595, 1592394909, 1174215055, 1290801793, - -1418998981, -1570324427, -1183720153, -1333995991, - -1889540349, -2041385971, -1656360673, -1807156719, - -486304949, -368142267, -249985705, -132870567, - -952647821, -835013507, -718427793, -601841055, - 1986918061, 2137062819, 1685577905, 1836772287, - 1381620373, 1532285339, 1078185097, 1229899655, - 1040559837, 923313619, 740276417, 621982671, - 439452389, 322734571, 137073913, 19308535, - -423803315, -273658557, -190361519, -39167137, - -1031181707, -880516741, -795640727, -643926169, - -1361764803, -1479011021, -1127282655, -1245576401, - -1964953083, -2081670901, -1728371687, -1846137065, - 1305906550, 1155237496, 1607244650, 1455525988, - 1776460110, 1626319424, 2079897426, 1928707164, - 96392454, 213114376, 396673818, 514443284, - 562755902, 679998000, 865136418, 983426092, - -586793578, -737462632, -820237430, -971956092, - -114159186, -264299872, -349698126, -500888388, - -1787927066, -1671205144, -2022411270, -1904641804, - -1319482914, -1202240816, -1556062270, -1437772596, - -321194175, -438830001, -20913827, -137500077, - -923870343, -1042034569, -621490843, -738605461, - -1531793615, -1379949505, -1230456531, -1079659997, - -2138668279, -1987344377, -1835231979, -1684955621, - 2081048481, 1963412655, 1846563261, 1729977011, - 1480485785, 1362321559, 1243905413, 1126790795, - 878845905, 1030690015, 645401037, 796197571, - 274084841, 425408743, 38544885, 188821243, - -681472870, -563312748, -981755258, -864644728, - -212492126, -94852180, -514869570, -398279248, - -1626745622, -1778065436, -1928084746, -2078357000, - -1153566510, -1305414692, -1457000754, -1607801408, - 1202797690, 1320957812, 1437280870, 1554391400, - 1669664834, 1787304780, 1906247262, 2022837584, - 265905162, 114585348, 499347990, 349075736, - 736970802, 585122620, 972512814, 821712160, - -1699282452, -1816524062, -2001922064, -2120213250, - -1098699308, -1215420710, -1399243832, -1517014842, - -757114468, -606973294, -1060810880, -909622130, - -152341084, -1671510, -453942344, -302225226, - 174567692, 57326082, 410887952, 292596766, - 777231668, 660510266, 1011452712, 893681702, - 1108339068, 1258480242, 1343618912, 1494807662, - 1715193156, 1865862730, 1948373848, 2100090966, - -1593017801, -1476300487, -1290376149, -1172609243, - -2059905521, -1942659839, -1759363053, -1641067747, - -379313593, -529979063, -75615141, -227328171, - -850391425, -1000536719, -548792221, -699985043, - 836553431, 953270745, 600235211, 718002117, - 367585007, 484830689, 133361907, 251657213, - 2041877159, 1891211689, 1806599355, 1654886325, - 1568718495, 1418573201, 1335535747, 1184342925 -}; - -const char CRijndael::sm_rcon[30] = -{ - 1, 2, 4, 8, 16, 32, - 64, -128, 27, 54, 108, -40, - -85, 77, -102, 47, 94, -68, - 99, -58, -105, 53, 106, -44, - -77, 125, -6, -17, -59, -111 -}; - -const int CRijndael::sm_shifts[3][4][2] = -{ - { {0, 0}, {1, 3}, {2, 2}, {3, 1} }, - { {0, 0}, {1, 5}, {2, 4}, {3, 3} }, - { {0, 0}, {1, 7}, {3, 5}, {4, 4} } -}; - -//Error Messages -char const* CRijndael::sm_szErrorMsg1 = "Object not Initialized"; -char const* CRijndael::sm_szErrorMsg2 = "Data not multiple of Block Size"; - -//Null chain -char const* CRijndael::sm_chain0 = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -//CONSTRUCTOR -CRijndael::CRijndael() : m_bKeyInit(false) -{ -} - -//DESTRUCTOR -CRijndael::~CRijndael() -{ -} - -//Expand a user-supplied key material into a session key. -// key - The 128/192/256-bit user-key to use. -// chain - initial chain block for CBC and CFB modes. -// keylength - 16, 24 or 32 bytes -// blockSize - The block size in bytes of this Rijndael (16, 24 or 32 bytes). -void CRijndael::MakeKey(char const* key, char const* chain, int keylength, int blockSize) -{ - if(NULL == key) - throw std::string("Empty key"); - if(!(16==keylength || 24==keylength || 32==keylength)) - throw std::string("Incorrect key length"); - if(!(16==blockSize || 24==blockSize || 32==blockSize)) - throw std::string("Incorrect block length"); - m_keylength = keylength; - m_blockSize = blockSize; - //Initialize the chain - memcpy(m_chain0, chain, m_blockSize); - memcpy(m_chain, chain, m_blockSize); - //Calculate Number of Rounds - switch(m_keylength) - { - case 16: - m_iROUNDS = (m_blockSize == 16) ? 10 : (m_blockSize == 24 ? 12 : 14); - break; - - case 24: - m_iROUNDS = (m_blockSize != 32) ? 12 : 14; - break; - - default: // 32 bytes = 256 bits - m_iROUNDS = 14; - } - int BC = m_blockSize / 4; - int i, j; - for(i=0; i<=m_iROUNDS; i++) - { - for(j=0; j> 16) & 0xFF] & 0xFF) << 24 ^ - (sm_S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ - (sm_S[ tt & 0xFF] & 0xFF) << 8 ^ - (sm_S[(tt >> 24) & 0xFF] & 0xFF) ^ - (sm_rcon[rconpointer++] & 0xFF) << 24; - if(KC != 8) - for(i=1, j=0; i> 8) & 0xFF] & 0xFF) << 8 ^ - (sm_S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ - (sm_S[(tt >> 24) & 0xFF] & 0xFF) << 24; - for(j = KC/2, i=j+1; i> 24) & 0xFF] ^ - sm_U2[(tt >> 16) & 0xFF] ^ - sm_U3[(tt >> 8) & 0xFF] ^ - sm_U4[tt & 0xFF]; - } - m_bKeyInit = true; -} - -//Convenience method to encrypt exactly one block of plaintext, assuming -//Rijndael's default block size (128-bit). -// in - The plaintext -// result - The ciphertext generated from a plaintext using the key -void CRijndael::DefEncryptBlock(char const* in, char* result) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - int* Ker = m_Ke[0]; - int t0 = ((unsigned char)*(in++) << 24); - t0 |= ((unsigned char)*(in++) << 16); - t0 |= ((unsigned char)*(in++) << 8); - (t0 |= (unsigned char)*(in++)) ^= Ker[0]; - int t1 = ((unsigned char)*(in++) << 24); - t1 |= ((unsigned char)*(in++) << 16); - t1 |= ((unsigned char)*(in++) << 8); - (t1 |= (unsigned char)*(in++)) ^= Ker[1]; - int t2 = ((unsigned char)*(in++) << 24); - t2 |= ((unsigned char)*(in++) << 16); - t2 |= ((unsigned char)*(in++) << 8); - (t2 |= (unsigned char)*(in++)) ^= Ker[2]; - int t3 = ((unsigned char)*(in++) << 24); - t3 |= ((unsigned char)*(in++) << 16); - t3 |= ((unsigned char)*(in++) << 8); - (t3 |= (unsigned char)*(in++)) ^= Ker[3]; - int a0, a1, a2, a3; - //Apply Round Transforms - for (int r = 1; r < m_iROUNDS; r++) - { - Ker = m_Ke[r]; - a0 = (sm_T1[(t0 >> 24) & 0xFF] ^ - sm_T2[(t1 >> 16) & 0xFF] ^ - sm_T3[(t2 >> 8) & 0xFF] ^ - sm_T4[t3 & 0xFF]) ^ Ker[0]; - a1 = (sm_T1[(t1 >> 24) & 0xFF] ^ - sm_T2[(t2 >> 16) & 0xFF] ^ - sm_T3[(t3 >> 8) & 0xFF] ^ - sm_T4[t0 & 0xFF]) ^ Ker[1]; - a2 = (sm_T1[(t2 >> 24) & 0xFF] ^ - sm_T2[(t3 >> 16) & 0xFF] ^ - sm_T3[(t0 >> 8) & 0xFF] ^ - sm_T4[t1 & 0xFF]) ^ Ker[2]; - a3 = (sm_T1[(t3 >> 24) & 0xFF] ^ - sm_T2[(t0 >> 16) & 0xFF] ^ - sm_T3[(t1 >> 8) & 0xFF] ^ - sm_T4[t2 & 0xFF]) ^ Ker[3]; - t0 = a0; - t1 = a1; - t2 = a2; - t3 = a3; - } - //Last Round is special - Ker = m_Ke[m_iROUNDS]; - int tt = Ker[0]; - result[0] = sm_S[(t0 >> 24) & 0xFF] ^ (tt >> 24); - result[1] = sm_S[(t1 >> 16) & 0xFF] ^ (tt >> 16); - result[2] = sm_S[(t2 >> 8) & 0xFF] ^ (tt >> 8); - result[3] = sm_S[t3 & 0xFF] ^ tt; - tt = Ker[1]; - result[4] = sm_S[(t1 >> 24) & 0xFF] ^ (tt >> 24); - result[5] = sm_S[(t2 >> 16) & 0xFF] ^ (tt >> 16); - result[6] = sm_S[(t3 >> 8) & 0xFF] ^ (tt >> 8); - result[7] = sm_S[t0 & 0xFF] ^ tt; - tt = Ker[2]; - result[8] = sm_S[(t2 >> 24) & 0xFF] ^ (tt >> 24); - result[9] = sm_S[(t3 >> 16) & 0xFF] ^ (tt >> 16); - result[10] = sm_S[(t0 >> 8) & 0xFF] ^ (tt >> 8); - result[11] = sm_S[t1 & 0xFF] ^ tt; - tt = Ker[3]; - result[12] = sm_S[(t3 >> 24) & 0xFF] ^ (tt >> 24); - result[13] = sm_S[(t0 >> 16) & 0xFF] ^ (tt >> 16); - result[14] = sm_S[(t1 >> 8) & 0xFF] ^ (tt >> 8); - result[15] = sm_S[t2 & 0xFF] ^ tt; -} - -//Convenience method to decrypt exactly one block of plaintext, assuming -//Rijndael's default block size (128-bit). -// in - The ciphertext. -// result - The plaintext generated from a ciphertext using the session key. -void CRijndael::DefDecryptBlock(char const* in, char* result) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - int* Kdr = m_Kd[0]; - int t0 = ((unsigned char)*(in++) << 24); - t0 = t0 | ((unsigned char)*(in++) << 16); - t0 |= ((unsigned char)*(in++) << 8); - (t0 |= (unsigned char)*(in++)) ^= Kdr[0]; - int t1 = ((unsigned char)*(in++) << 24); - t1 |= ((unsigned char)*(in++) << 16); - t1 |= ((unsigned char)*(in++) << 8); - (t1 |= (unsigned char)*(in++)) ^= Kdr[1]; - int t2 = ((unsigned char)*(in++) << 24); - t2 |= ((unsigned char)*(in++) << 16); - t2 |= ((unsigned char)*(in++) << 8); - (t2 |= (unsigned char)*(in++)) ^= Kdr[2]; - int t3 = ((unsigned char)*(in++) << 24); - t3 |= ((unsigned char)*(in++) << 16); - t3 |= ((unsigned char)*(in++) << 8); - (t3 |= (unsigned char)*(in++)) ^= Kdr[3]; - int a0, a1, a2, a3; - for(int r = 1; r < m_iROUNDS; r++) // apply round transforms - { - Kdr = m_Kd[r]; - a0 = (sm_T5[(t0 >> 24) & 0xFF] ^ - sm_T6[(t3 >> 16) & 0xFF] ^ - sm_T7[(t2 >> 8) & 0xFF] ^ - sm_T8[ t1 & 0xFF] ) ^ Kdr[0]; - a1 = (sm_T5[(t1 >> 24) & 0xFF] ^ - sm_T6[(t0 >> 16) & 0xFF] ^ - sm_T7[(t3 >> 8) & 0xFF] ^ - sm_T8[ t2 & 0xFF] ) ^ Kdr[1]; - a2 = (sm_T5[(t2 >> 24) & 0xFF] ^ - sm_T6[(t1 >> 16) & 0xFF] ^ - sm_T7[(t0 >> 8) & 0xFF] ^ - sm_T8[ t3 & 0xFF] ) ^ Kdr[2]; - a3 = (sm_T5[(t3 >> 24) & 0xFF] ^ - sm_T6[(t2 >> 16) & 0xFF] ^ - sm_T7[(t1 >> 8) & 0xFF] ^ - sm_T8[ t0 & 0xFF] ) ^ Kdr[3]; - t0 = a0; - t1 = a1; - t2 = a2; - t3 = a3; - } - //Last Round is special - Kdr = m_Kd[m_iROUNDS]; - int tt = Kdr[0]; - result[ 0] = sm_Si[(t0 >> 24) & 0xFF] ^ (tt >> 24); - result[ 1] = sm_Si[(t3 >> 16) & 0xFF] ^ (tt >> 16); - result[ 2] = sm_Si[(t2 >> 8) & 0xFF] ^ (tt >> 8); - result[ 3] = sm_Si[ t1 & 0xFF] ^ tt; - tt = Kdr[1]; - result[ 4] = sm_Si[(t1 >> 24) & 0xFF] ^ (tt >> 24); - result[ 5] = sm_Si[(t0 >> 16) & 0xFF] ^ (tt >> 16); - result[ 6] = sm_Si[(t3 >> 8) & 0xFF] ^ (tt >> 8); - result[ 7] = sm_Si[ t2 & 0xFF] ^ tt; - tt = Kdr[2]; - result[ 8] = sm_Si[(t2 >> 24) & 0xFF] ^ (tt >> 24); - result[ 9] = sm_Si[(t1 >> 16) & 0xFF] ^ (tt >> 16); - result[10] = sm_Si[(t0 >> 8) & 0xFF] ^ (tt >> 8); - result[11] = sm_Si[ t3 & 0xFF] ^ tt; - tt = Kdr[3]; - result[12] = sm_Si[(t3 >> 24) & 0xFF] ^ (tt >> 24); - result[13] = sm_Si[(t2 >> 16) & 0xFF] ^ (tt >> 16); - result[14] = sm_Si[(t1 >> 8) & 0xFF] ^ (tt >> 8); - result[15] = sm_Si[ t0 & 0xFF] ^ tt; -} - -//Encrypt exactly one block of plaintext. -// in - The plaintext. -// result - The ciphertext generated from a plaintext using the key. -void CRijndael::EncryptBlock(char const* in, char* result) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - if(DEFAULT_BLOCK_SIZE == m_blockSize) - { - DefEncryptBlock(in, result); - return; - } - int BC = m_blockSize / 4; - int SC = (BC == 4) ? 0 : (BC == 6 ? 1 : 2); - int s1 = sm_shifts[SC][1][0]; - int s2 = sm_shifts[SC][2][0]; - int s3 = sm_shifts[SC][3][0]; - //Temporary Work Arrays - int i; - int tt; - int* pi = t; - for(i=0; i> 24) & 0xFF] ^ - sm_T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ - sm_T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ - sm_T4[ t[(i + s3) % BC] & 0xFF] ) ^ m_Ke[r][i]; - memcpy(t, a, 4*BC); - } - int j; - //Last Round is Special - for(i=0,j=0; i> 24) & 0xFF] ^ (tt >> 24); - result[j++] = sm_S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16); - result[j++] = sm_S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8); - result[j++] = sm_S[ t[(i + s3) % BC] & 0xFF] ^ tt; - } -} - -//Decrypt exactly one block of ciphertext. -// in - The ciphertext. -// result - The plaintext generated from a ciphertext using the session key. -void CRijndael::DecryptBlock(char const* in, char* result) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - if(DEFAULT_BLOCK_SIZE == m_blockSize) - { - DefDecryptBlock(in, result); - return; - } - int BC = m_blockSize / 4; - int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2); - int s1 = sm_shifts[SC][1][1]; - int s2 = sm_shifts[SC][2][1]; - int s3 = sm_shifts[SC][3][1]; - //Temporary Work Arrays - int i; - int tt; - int* pi = t; - for(i=0; i> 24) & 0xFF] ^ - sm_T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ - sm_T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ - sm_T8[ t[(i + s3) % BC] & 0xFF]) ^ m_Kd[r][i]; - memcpy(t, a, 4*BC); - } - int j; - //Last Round is Special - for(i=0,j=0; i> 24) & 0xFF] ^ (tt >> 24); - result[j++] = sm_Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16); - result[j++] = sm_Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8); - result[j++] = sm_Si[ t[(i + s3) % BC] & 0xFF] ^ tt; - } -} - -void CRijndael::Encrypt(char const* in, char* result, size_t n, int iMode) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - //n should be > 0 and multiple of m_blockSize - if(0==n || n%m_blockSize!=0) - throw std::string(sm_szErrorMsg2); - int i; - char const* pin; - char* presult; - if(CBC == iMode) //CBC mode, using the Chain - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - Xor(m_chain, pin); - EncryptBlock(m_chain, presult); - memcpy(m_chain, presult, m_blockSize); - pin += m_blockSize; - presult += m_blockSize; - } - } - else if(CFB == iMode) //CFB mode, using the Chain - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - EncryptBlock(m_chain, presult); - Xor(presult, pin); - memcpy(m_chain, presult, m_blockSize); - pin += m_blockSize; - presult += m_blockSize; - } - } - else //ECB mode, not using the Chain - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - EncryptBlock(pin, presult); - pin += m_blockSize; - presult += m_blockSize; - } - } -} - -void CRijndael::Decrypt(char const* in, char* result, size_t n, int iMode) -{ - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - //n should be > 0 and multiple of m_blockSize - if(0==n || n%m_blockSize!=0) - throw std::string(sm_szErrorMsg2); - int i; - char const* pin; - char* presult; - if(CBC == iMode) //CBC mode, using the Chain - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - DecryptBlock(pin, presult); - Xor(presult, m_chain); - memcpy(m_chain, pin, m_blockSize); - pin += m_blockSize; - presult += m_blockSize; - } - } - else if(CFB == iMode) //CFB mode, using the Chain, not using Decrypt() - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - EncryptBlock(m_chain, presult); - //memcpy(presult, pin, m_blockSize); - Xor(presult, pin); - memcpy(m_chain, pin, m_blockSize); - pin += m_blockSize; - presult += m_blockSize; - } - } - else //ECB mode, not using the Chain - { - for(i=0,pin=in,presult=result; i<(int)n/m_blockSize; i++) - { - DecryptBlock(pin, presult); - pin += m_blockSize; - presult += m_blockSize; - } - } -} diff --git a/indra/llimage/aes.h b/indra/llimage/aes.h deleted file mode 100644 index 37e4aa5a8..000000000 --- a/indra/llimage/aes.h +++ /dev/null @@ -1,190 +0,0 @@ - -//Rijndael.h - -#ifndef __RIJNDAEL_H__ -#define __RIJNDAEL_H__ - -#include -#include -#include - -using namespace std; - -//Rijndael (pronounced Reindaal) is a block cipher, designed by Joan Daemen and Vincent Rijmen as a candidate algorithm for the AES. -//The cipher has a variable block length and key length. The authors currently specify how to use keys with a length -//of 128, 192, or 256 bits to encrypt blocks with al length of 128, 192 or 256 bits (all nine combinations of -//key length and block length are possible). Both block length and key length can be extended very easily to -// multiples of 32 bits. -//Rijndael can be implemented very efficiently on a wide range of processors and in hardware. -//This implementation is based on the Java Implementation used with the Cryptix toolkit found at: -//http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael.zip -//Java code authors: Raif S. Naffah, Paulo S. L. M. Barreto -//This Implementation was tested against KAT test published by the authors of the method and the -//results were identical. -class CRijndael -{ -public: - //Operation Modes - //The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback Block (CFB) modes - //are implemented. - //In ECB mode if the same block is encrypted twice with the same key, the resulting - //ciphertext blocks are the same. - //In CBC Mode a ciphertext block is obtained by first xoring the - //plaintext block with the previous ciphertext block, and encrypting the resulting value. - //In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block - //and xoring the resulting value with the plaintext. - enum { ECB=0, CBC=1, CFB=2 }; - -private: - enum { DEFAULT_BLOCK_SIZE=16 }; - enum { MAX_BLOCK_SIZE=32, MAX_ROUNDS=14, MAX_KC=8, MAX_BC=8 }; - - //Auxiliary Functions - //Multiply two elements of GF(2^m) - static int Mul(int a, int b) - { - return (a != 0 && b != 0) ? sm_alog[(sm_log[a & 0xFF] + sm_log[b & 0xFF]) % 255] : 0; - } - - //Convenience method used in generating Transposition Boxes - static int Mul4(int a, char b[]) - { - if(a == 0) - return 0; - a = sm_log[a & 0xFF]; - int a0 = (b[0] != 0) ? sm_alog[(a + sm_log[b[0] & 0xFF]) % 255] & 0xFF : 0; - int a1 = (b[1] != 0) ? sm_alog[(a + sm_log[b[1] & 0xFF]) % 255] & 0xFF : 0; - int a2 = (b[2] != 0) ? sm_alog[(a + sm_log[b[2] & 0xFF]) % 255] & 0xFF : 0; - int a3 = (b[3] != 0) ? sm_alog[(a + sm_log[b[3] & 0xFF]) % 255] & 0xFF : 0; - return a0 << 24 | a1 << 16 | a2 << 8 | a3; - } - -public: - //CONSTRUCTOR - CRijndael(); - - //DESTRUCTOR - virtual ~CRijndael(); - - //Expand a user-supplied key material into a session key. - // key - The 128/192/256-bit user-key to use. - // chain - initial chain block for CBC and CFB modes. - // keylength - 16, 24 or 32 bytes - // blockSize - The block size in bytes of this Rijndael (16, 24 or 32 bytes). - void MakeKey(char const* key, char const* chain, int keylength=DEFAULT_BLOCK_SIZE, int blockSize=DEFAULT_BLOCK_SIZE); - -private: - //Auxiliary Function - void Xor(char* buff, char const* chain) - { - if(false==m_bKeyInit) - throw std::string(sm_szErrorMsg1); - for(int i=0; i -#include "linden_common.h" -#include "llimagemetadatareader.h" -#include "aes.h" -//#include "llapr.h" -//#include "llerror.h" -const unsigned char EMKDU_AES_KEY[] = {0x01,0x00,0x81,0x07,0x63,0x78,0xB6,0xFE,0x6E,0x3F,0xB0,0x12,0xCC,0x65,0x66,0xC1, -0x81,0x96,0xAC,0xC1,0x3B,0x66,0x0B,0xF7}; -//#define COMMENT_DEBUGG1ING -LLJ2cParser::LLJ2cParser(U8* data,int data_size) -{ - if(data && data_size) - { - mData.resize(data_size); - memcpy(&(mData[0]), data, data_size); - //std::copy(data,data+data_size,mData.begin()); - } - mIter = mData.begin(); -} - -U8 LLJ2cParser::nextChar() -{ - U8 rtn = 0x00; - if(mIter != mData.end()) - { - rtn = (*mIter); - mIter++; - } - return rtn; -} - -std::vector LLJ2cParser::nextCharArray(int len) -{ - std::vector array; - if(len > 0) - { - array.resize(len); - for(S32 i = 0; i < len; i++) - { - array[i] = nextChar(); - } - } - return array; -} - -std::vector LLJ2cParser::GetNextComment() -{ - std::vector content; - while (mIter != mData.end()) - { - U8 marker = nextChar(); - if (marker == 0xff) - { - U8 marker_type = nextChar(); - if (marker_type == 0x4f) - { - continue; - } - if (marker_type == 0x90) - { - //llinfos << "FOUND 0x90" << llendl; - break; //return empty vector - } - - if (marker_type == 0x64) - { - //llinfos << "FOUND 0x64 COMMENT SECTION" << llendl; - S32 len = ((S32)nextChar())*256 + (S32)nextChar(); - if (len > 3) content = nextCharArray(len - 2); - return content; - } - } - } - content.clear(); //return empty vector by clear anything there - return content; -} - -//flow of control in this method is shit, gotta fix this... possibly return a vector or map instead of a string -HG - -/* - Notes: - - For anyone debugging this method, if a comment is not being decoded properly and you know encryption is being used, - the easiest thing to do is to create an LLAPRFile handle inside this method and write the contents of data to a file. - Normally the comment is going to be up near the header, just have a look at it in a hex editor. - - It's generally going to be a string of 130 bytes preceeded by a null. -*/ - -//static -unsigned int LLImageMetaDataReader::ExtractEncodedComment(U8* data,int data_size, std::string& output) -{ - LLJ2cParser parser = LLJ2cParser(data,data_size); - - std::string decodedComment; - - //not supported yet, but why the hell not? - unsigned int result = ENC_NONE; - - while(1) - { - std::vector comment = parser.GetNextComment(); - if (comment.empty()) break; //exit loop - - if (comment[1] == 0x00 && comment.size() == 130) - { - bool xorComment = true; - //llinfos << "FOUND PAYLOAD" << llendl; - std::vector payload(128); - S32 i; - memcpy(&(payload[0]), &(comment[2]), 128); - //std::copy(comment.begin()+2,comment.end(),payload.begin()); - //lets check xorComment Cipher first - if (payload[2] == payload[127]) - { - // emkdu.dll - for (i = 4; i < 128; i += 4) - { - payload[i] ^= payload[3]; - payload[i + 1] ^= payload[1]; - payload[i + 2] ^= payload[0]; - payload[i + 3] ^= payload[2]; - } - result = ENC_EMKDU_V1; - } - else if (payload[3] == payload[127]) - { - // emkdu.dll or onyxkdu.dll - for (i = 4; i < 128; i += 4) - { - payload[i] ^= payload[2]; - payload[i + 1] ^= payload[0]; - payload[i + 2] ^= payload[1]; - payload[i + 3] ^= payload[3]; - } - result = ENC_ONYXKDU; - } - else - { - xorComment = false; - } - if(!xorComment) - { - //this is terrible i know - std::vector decrypted(129); - CRijndael aes; - try - { - aes.MakeKey(reinterpret_cast(EMKDU_AES_KEY),"", 24, 16); - } catch(std::string error) - { - llinfos << error << llendl; - } - try - { - int numBlocks = 8; - char* datain = (char*)&(payload[0]); - char* dataout = (char*)&(decrypted[0]); - char buffer[64]; - memset(buffer,0,sizeof(buffer)); - aes.DecryptBlock(datain,dataout); // do first block - for (int pos = 0; pos < 16; ++pos) - *dataout++ ^= buffer[pos]; - datain += 16; - numBlocks--; - - while (numBlocks) - { - aes.DecryptBlock(datain,dataout); // do next block - for (int pos = 0; pos < 16; ++pos) - *dataout++ ^= *(datain-16+pos); - datain += 16; - --numBlocks; - } - } catch(std::string error) - { - llinfos << error << llendl; - } - //payload.clear(); - //memcpy(&(payload[0]),&(dataout[0]),dataout.size()); - for (i = 0 ; i < 128; ++i) - { - if (decrypted[i] == 0) break; - } - if(i == 0) continue; - if(decodedComment.length() > 0) - decodedComment.append(", "); - - //the way it's being done now, you can only specify the encryption type for the last comment. - //need to switch to a map or a vector for output. - result = ENC_EMKDU_V2; - decodedComment.append(decrypted.begin(),decrypted.begin()+i); - } - else - { - for (i = 4 ; i < 128; ++i) - { - if (payload[i] == 0) break; - } - if(i < 4) continue; - if(decodedComment.length() > 0) - decodedComment.append(", "); - - decodedComment.append(payload.begin()+4,payload.begin()+i); - } - //llinfos << "FOUND COMMENT: " << result << llendl; - } - } - //end of loop - output = decodedComment; - return result; -} -// diff --git a/indra/llimage/llimagemetadatareader.h b/indra/llimage/llimagemetadatareader.h deleted file mode 100644 index b72c43e80..000000000 --- a/indra/llimage/llimagemetadatareader.h +++ /dev/null @@ -1,32 +0,0 @@ -// -#ifndef LL_LLIMAGEMETADATAREADER_H -#define LL_LLIMAGEMETADATAREADER_H -#include "stdtypes.h" -#include -#include -#include - -//encryption types -#define ENC_NONE 0 -#define ENC_ONYXKDU 1 -#define ENC_EMKDU_V1 2 -#define ENC_EMKDU_V2 4 - -class LLJ2cParser -{ -public: - LLJ2cParser(U8* data,int data_size); - std::vector GetNextComment(); - std::vector mData; -private: - U8 nextChar(); - std::vector nextCharArray(int len); - std::vector::iterator mIter; -}; -class LLImageMetaDataReader -{ -public: - static unsigned int ExtractEncodedComment(U8* data,int data_size, std::string& output); -}; -#endif -// diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index f54d2c761..265371cc8 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2440,7 +2440,6 @@ BOOL LLLineEditor::evaluateFloat() { bool success = false; std::string expr = getText(); - LLStringUtil::toUpper(expr); // user deleted the contents, nothing to evaluate -- MC if (expr.empty()) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 4b27f6a2a..9c90668c6 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -54,12 +54,10 @@ #include "lldriverparam.h" #include "lleditingmotion.h" #include "llemote.h" - #include "llfirstuse.h" #include "llfloaterchat.h" #include "llfloaterinventory.h" #include "llheadrotmotion.h" - #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llinventorybridge.h" @@ -112,7 +110,6 @@ // #include "llfloaterexploreanimations.h" -#include "llimagemetadatareader.h" // #include "llavatarname.h" From 5d5c3e2a481f182207accc766ff6a6890716c7c7 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 17:56:43 -0400 Subject: [PATCH 120/123] LLCalcparser update --- indra/llmath/CMakeLists.txt | 7 +- indra/llmath/llcalc.cpp | 169 +++++++++--------- indra/llmath/llcalc.h | 30 +++- indra/llmath/llcalcparser.h | 344 ++++++++++++++++++++---------------- 4 files changed, 305 insertions(+), 245 deletions(-) diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 584abdf89..c168f7a89 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -12,8 +12,7 @@ include_directories( set(llmath_SOURCE_FILES llbbox.cpp llbboxlocal.cpp - llcalc.cpp - llcalcparser.cpp + llcalc.cpp llcamera.cpp llcoordframe.cpp llline.cpp @@ -48,8 +47,8 @@ set(llmath_HEADER_FILES coordframe.h llbbox.h llbboxlocal.h - llcalc.h - llcalcparser.h + llcalc.h + llcalcparser.h llcamera.h llcoord.h llcoordframe.h diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index 597d0815f..331e363e7 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -1,9 +1,26 @@ /* * LLCalc.cpp - * SecondLife - * - * Created by Aimee Walton on 28/09/2008. - * Copyright 2008 Aimee Walton. + * Copyright 2008 Aimee Walton. + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2008, 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$ * */ @@ -11,59 +28,61 @@ #include "llcalc.h" -#include "llcalcparser.h" #include "llmath.h" +#include "llcalcparser.h" // Variable names for use in the build floater -const char* LLCalc::X_POS = "PX"; -const char* LLCalc::Y_POS = "PY"; -const char* LLCalc::Z_POS = "PZ"; -const char* LLCalc::X_SCALE = "SX"; -const char* LLCalc::Y_SCALE = "SY"; -const char* LLCalc::Z_SCALE = "SZ"; -const char* LLCalc::X_ROT = "RX"; -const char* LLCalc::Y_ROT = "RY"; -const char* LLCalc::Z_ROT = "RZ"; -const char* LLCalc::HOLLOW = "HLW"; -const char* LLCalc::CUT_BEGIN = "CB"; -const char* LLCalc::CUT_END = "CE"; -const char* LLCalc::PATH_BEGIN = "PB"; -const char* LLCalc::PATH_END = "PE"; -const char* LLCalc::TWIST_BEGIN = "TB"; -const char* LLCalc::TWIST_END = "TE"; -const char* LLCalc::X_SHEAR = "SHX"; -const char* LLCalc::Y_SHEAR = "SHY"; -const char* LLCalc::X_TAPER = "TPX"; -const char* LLCalc::Y_TAPER = "TPY"; -const char* LLCalc::RADIUS_OFFSET = "ROF"; -const char* LLCalc::REVOLUTIONS = "REV"; -const char* LLCalc::SKEW = "SKW"; -const char* LLCalc::X_HOLE = "HLX"; -const char* LLCalc::Y_HOLE = "HLY"; -const char* LLCalc::TEX_U_SCALE = "TSU"; -const char* LLCalc::TEX_V_SCALE = "TSV"; -const char* LLCalc::TEX_U_OFFSET = "TOU"; -const char* LLCalc::TEX_V_OFFSET = "TOV"; -const char* LLCalc::TEX_ROTATION = "TROT"; -const char* LLCalc::TEX_TRANSPARENCY = "TRNS"; -const char* LLCalc::TEX_GLOW = "GLOW"; - +// must be lower case for parser definition +// case-insensitive for actual parsing +const char* LLCalc::X_POS = "px"; +const char* LLCalc::Y_POS = "py"; +const char* LLCalc::Z_POS = "pz"; +const char* LLCalc::X_SCALE = "sx"; +const char* LLCalc::Y_SCALE = "sy"; +const char* LLCalc::Z_SCALE = "sz"; +const char* LLCalc::X_ROT = "rx"; +const char* LLCalc::Y_ROT = "ry"; +const char* LLCalc::Z_ROT = "rz"; +const char* LLCalc::HOLLOW = "hlw"; +const char* LLCalc::CUT_BEGIN = "cb"; +const char* LLCalc::CUT_END = "ce"; +const char* LLCalc::PATH_BEGIN = "pb"; +const char* LLCalc::PATH_END = "pe"; +const char* LLCalc::TWIST_BEGIN = "tb"; +const char* LLCalc::TWIST_END = "te"; +const char* LLCalc::X_SHEAR = "shx"; +const char* LLCalc::Y_SHEAR = "shy"; +const char* LLCalc::X_TAPER = "tpx"; +const char* LLCalc::Y_TAPER = "tpy"; +const char* LLCalc::RADIUS_OFFSET = "rof"; +const char* LLCalc::REVOLUTIONS = "rev"; +const char* LLCalc::SKEW = "skw"; +const char* LLCalc::X_HOLE = "hlx"; +const char* LLCalc::Y_HOLE = "hly"; +const char* LLCalc::TEX_U_SCALE = "tsu"; +const char* LLCalc::TEX_V_SCALE = "tsv"; +const char* LLCalc::TEX_U_OFFSET = "tou"; +const char* LLCalc::TEX_V_OFFSET = "tov"; +const char* LLCalc::TEX_ROTATION = "trot"; +const char* LLCalc::TEX_TRANSPARENCY = "trns"; +const char* LLCalc::TEX_GLOW = "glow"; LLCalc* LLCalc::sInstance = NULL; +//TODO: Make this a static global class LLCalc::LLCalc() : mLastErrorPos(0) { // Init table of constants - mConstants["PI"] = F_PI; - mConstants["TWO_PI"] = F_TWO_PI; - mConstants["PI_BY_TWO"] = F_PI_BY_TWO; - mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; - mConstants["SQRT2"] = F_SQRT2; - mConstants["SQRT3"] = F_SQRT3; - mConstants["DEG_TO_RAD"] = DEG_TO_RAD; - mConstants["RAD_TO_DEG"] = RAD_TO_DEG; - mConstants["GRAVITY"] = GRAVITY; + /*setVar("PI", F_PI); + setVar("TWO_PI", F_TWO_PI); + setVar("PI_BY_TWO", F_PI_BY_TWO); + setVar("SQRT_TWO_PI", F_SQRT_TWO_PI); + setVar("SQRT2", F_SQRT2); + setVar("SQRT3", F_SQRT3); + setVar("DEG_TO_RAD", DEG_TO_RAD); + setVar("RAD_TO_DEG", RAD_TO_DEG); + setVar("GRAVITY", GRAVITY);*/ } LLCalc::~LLCalc() @@ -80,7 +99,7 @@ void LLCalc::cleanUp() //static LLCalc* LLCalc::getInstance() { - if (!sInstance) sInstance = new LLCalc(); + if (!sInstance) sInstance = new LLCalc(); return sInstance; } @@ -99,47 +118,35 @@ void LLCalc::clearAllVariables() mVariables.clear(); } -/* -void LLCalc::updateVariables(LLSD& vars) -{ - LLSD::map_iterator cIt = vars.beginMap(); - for(; cIt != vars.endMap(); cIt++) - { - setVar(cIt->first, (F32)(LLSD::Real)cIt->second); - } -} -*/ - bool LLCalc::evalString(const std::string& expression, F32& result) { - std::string expr_upper = expression; - LLStringUtil::toUpper(expr_upper); - - LLCalcParser calc(result, &mConstants, &mVariables); - mLastErrorPos = 0; - std::string::iterator start = expr_upper.begin(); - parse_info info; - - try + std::string::const_iterator itr = expression.begin(); + expression::grammar calc; + calc.constant.add + ("pi", F_PI) + ("two_pi", F_TWO_PI) + ("pi_by_two", F_PI_BY_TWO) + ("sqrt_two_pi", F_SQRT_TWO_PI) + ("sqrt2", F_SQRT2) + ("sqrt3", F_SQRT3) + ("deg_to_rad", DEG_TO_RAD) + ("rad_to_deg", RAD_TO_DEG) + ("gravity", GRAVITY) + ; + for(calc_map_t::const_iterator iter = mVariables.begin(); + iter != mVariables.end(); + ++iter) { - info = parse(start, expr_upper.end(), calc, space_p); - lldebugs << "Math expression: " << expression << " = " << result << llendl; + calc.constant.add(iter->first, iter->second); } - catch(parser_error &e) + + if (!expression::parse(itr, expression.end(), calc, result) || itr != expression.end()) { - mLastErrorPos = e.where - expr_upper.begin(); - - llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl; - return false; - } - - if (!info.full) - { - mLastErrorPos = info.stop - expr_upper.begin(); + mLastErrorPos = itr - expression.begin(); llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl; return false; } - + lldebugs << "Math expression: " << expression << " = " << result << llendl; return true; } diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index 886e0bc8f..8c71569f4 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -1,9 +1,26 @@ /* * LLCalc.h - * SecondLife - * - * Created by Aimee Walton on 28/09/2008. * Copyright 2008 Aimee Walton. + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2008, 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$ * */ @@ -69,13 +86,8 @@ public: private: std::string::size_type mLastErrorPos; - calc_map_t mConstants; calc_map_t mVariables; - - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t* mUserVariables; - + // "There shall be only one" static LLCalc* sInstance; }; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index e0ad27026..0fcd38064 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -27,165 +27,207 @@ #ifndef LL_CALCPARSER_H #define LL_CALCPARSER_H -#include -#include -#include -#include -#include -#include -using namespace boost::spirit::classic; +#include +#if !defined(SPIRIT_VERSION) || SPIRIT_VERSION < 0x2010 +#error "At least Spirit version 2.1 required" +#endif -#include "llcalc.h" -#include "llmath.h" +// Add this in if we want boost math constants. +//#include +#include +#include -struct LLCalcParser : grammar +namespace expression { + + +//TODO: If we can find a better way to do this with boost::pheonix::bind lets do it +namespace { // anonymous + +template +T min_glue(T a, T b) { - LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : - mResult(result), mConstants(constants), mVariables(vars) {}; - - struct value_closure : closure - { - member1 value; - }; - - template - struct definition - { - // Rule declarations - rule statement, identifier; - rule expression, term, - power, - unary_expr, - factor, - unary_func, - binary_func, - group; + return std::min(a, b); +} - // start() should return the starting symbol - rule const& start() const { return statement; } - - definition(LLCalcParser const& self) +template +T max_glue(T a, T b) +{ + return std::max(a, b); +} + +struct lazy_pow_ +{ + template + struct result { typedef X type; }; + + template + X operator()(X x, Y y) const + { + return std::pow(x, y); + } +}; + +struct lazy_ufunc_ +{ + template + struct result { typedef A1 type; }; + + template + A1 operator()(F f, A1 a1) const + { + return f(a1); + } +}; + +struct lazy_bfunc_ +{ + template + struct result { typedef A1 type; }; + + template + A1 operator()(F f, A1 a1, A2 a2) const + { + return f(a1, a2); + } +}; + +} // end namespace anonymous + +template +struct grammar + : boost::spirit::qi::grammar< + Iterator, FPT(), boost::spirit::ascii::space_type + > +{ + + // symbol table for constants + // to be added by the actual calculator + struct constant_ + : boost::spirit::qi::symbols< + typename std::iterator_traits::value_type, + FPT + > + { + constant_() { - using namespace phoenix; - - assertion assert_domain("Domain error"); -// assertion assert_symbol("Unknown symbol"); - assertion assert_syntax("Syntax error"); - - identifier = - lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] - ; - - group = - '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) - ; - - unary_func = - ((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) | - (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) | - (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) | - (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) | - (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) | - (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) | - (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) | - (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) | - (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) | - (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) | - (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) | - (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - binary_func = - ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | - (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | - (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> - expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) - ) >> assert_syntax(ch_p(')')) - ; - - // *TODO: Localisation of the decimal point? - // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate - // for the current locale. However to do that here could clash with using - // the comma as a separator when passing arguments to functions. - factor = - (ureal_p[factor.value = arg1] | - group[factor.value = arg1] | - unary_func[factor.value = arg1] | - binary_func[factor.value = arg1] | - // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, - // would be "neater" to handle symbol lookup from here with an assertive parser. -// constants_p[factor.value = arg1]| - identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)] - ) >> - // Detect and throw math errors. - assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value))) - ; - - unary_expr = - !ch_p('+') >> factor[unary_expr.value = arg1] | - '-' >> factor[unary_expr.value = -arg1] - ; - - power = - unary_expr[power.value = arg1] >> - *('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)])) - ; - - term = - power[term.value = arg1] >> - *(('*' >> assert_syntax(power[term.value *= arg1])) | - ('/' >> assert_syntax(power[term.value /= arg1])) | - ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)])) - ) - ; - - expression = - assert_syntax(term[expression.value = arg1]) >> - *(('+' >> assert_syntax(term[expression.value += arg1])) | - ('-' >> assert_syntax(term[expression.value -= arg1])) - ) - ; - - statement = - !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) + } + } constant; + + // symbol table for unary functions like "abs" + struct ufunc_ + : boost::spirit::qi::symbols< + typename std::iterator_traits::value_type, + FPT (*)(FPT) + > + { + ufunc_() + { + this->add + ("abs" , (FPT (*)(FPT)) std::abs ) + ("acos" , (FPT (*)(FPT)) std::acos ) + ("asin" , (FPT (*)(FPT)) std::asin ) + ("atan" , (FPT (*)(FPT)) std::atan ) + ("ceil" , (FPT (*)(FPT)) std::ceil ) + ("cos" , (FPT (*)(FPT)) std::cos ) + ("cosh" , (FPT (*)(FPT)) std::cosh ) + ("exp" , (FPT (*)(FPT)) std::exp ) + ("floor" , (FPT (*)(FPT)) std::floor) + ("log" , (FPT (*)(FPT)) std::log ) + ("log10" , (FPT (*)(FPT)) std::log10) + ("sin" , (FPT (*)(FPT)) std::sin ) + ("sinh" , (FPT (*)(FPT)) std::sinh ) + ("sqrt" , (FPT (*)(FPT)) std::sqrt ) + ("tan" , (FPT (*)(FPT)) std::tan ) + ("tanh" , (FPT (*)(FPT)) std::tanh ) ; } - }; - -private: - // Member functions for semantic actions - F32 lookup(const std::string::iterator&, const std::string::iterator&) const; - F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } - F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } - - bool checkNaN(const F32& a) const { return !llisnan(a); } - - //FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz - F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } - F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } - F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } - F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); } - F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); } - F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); } - F32 _sqrt(const F32& a) const { return sqrt(a); } - F32 _log(const F32& a) const { return log(a); } - F32 _exp(const F32& a) const { return exp(a); } - F32 _fabs(const F32& a) const { return fabs(a); } - F32 _floor(const F32& a) const { return (F32)llfloor(a); } - F32 _ceil(const F32& a) const { return llceil(a); } + } ufunc; - F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } - - - - LLCalc::calc_map_t* mConstants; - LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; - - F32& mResult; + // symbol table for binary functions like "pow" + struct bfunc_ + : boost::spirit::qi::symbols< + typename std::iterator_traits::value_type, + FPT (*)(FPT, FPT) + > + { + bfunc_() + { + using boost::bind; + this->add + ("pow" , (FPT (*)(FPT, FPT)) std::pow ) + ("atan2", (FPT (*)(FPT, FPT)) std::atan2) + ("min" , (FPT (*)(FPT, FPT)) min_glue) + ("max" , (FPT (*)(FPT, FPT)) max_glue) + ; + } + } bfunc; + + boost::spirit::qi::rule< + Iterator, FPT(), boost::spirit::ascii::space_type + > expression, term, factor, primary; + + grammar() : grammar::base_type(expression) + { + using boost::spirit::qi::real_parser; + using boost::spirit::qi::real_policies; + real_parser > real; + + using boost::spirit::qi::_1; + using boost::spirit::qi::_2; + using boost::spirit::qi::_3; + using boost::spirit::qi::no_case; + using boost::spirit::qi::_val; + + boost::phoenix::function lazy_pow; + boost::phoenix::function lazy_ufunc; + boost::phoenix::function lazy_bfunc; + + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + primary [_val = _1] + >> *( ("**" >> factor [_val = lazy_pow(_val, _1)]) + ) + ; + + primary = + real [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> primary [_val = -_1]) + | ('+' >> primary [_val = _1]) + | (no_case[ufunc] >> '(' >> expression >> ')') + [_val = lazy_ufunc(_1, _2)] + | (no_case[bfunc] >> '(' >> expression >> ',' + >> expression >> ')') + [_val = lazy_bfunc(_1, _2, _3)] + | no_case[constant] [_val = _1] + ; + + } }; + +template +bool parse(Iterator &iter, + Iterator end, + const grammar &g, + FPT &result) +{ + return boost::spirit::qi::phrase_parse( + iter, end, g, boost::spirit::ascii::space, result); +} + +} // end namespace expression #endif // LL_CALCPARSER_H From f84f4aa058ebd51bb2a1fbc34c73c6ddc5473348 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 18:13:48 -0400 Subject: [PATCH 121/123] Updated/Fixed Cursors --- .../cursors_mac/UI_CURSOR_PATHFINDING.tif | Bin 0 -> 504 bytes .../cursors_mac/UI_CURSOR_PATHFINDING_END.tif | Bin 0 -> 556 bytes .../UI_CURSOR_PATHFINDING_END_ADD.tif | Bin 0 -> 570 bytes .../UI_CURSOR_PATHFINDING_START.tif | Bin 0 -> 532 bytes .../UI_CURSOR_PATHFINDING_START_ADD.tif | Bin 0 -> 550 bytes indra/newview/res-sdl/lltoolpathfinding.BMP | Bin 0 -> 3126 bytes .../res-sdl/lltoolpathfindingpathend.BMP | Bin 0 -> 3126 bytes .../res-sdl/lltoolpathfindingpathendadd.BMP | Bin 0 -> 3126 bytes .../res-sdl/lltoolpathfindingpathstart.BMP | Bin 0 -> 3126 bytes .../res-sdl/lltoolpathfindingpathstartadd.BMP | Bin 0 -> 3126 bytes indra/newview/res-sdl/toolbuy.BMP | Bin 3126 -> 3126 bytes indra/newview/res-sdl/toolbuy_old.BMP | Bin 0 -> 3126 bytes indra/newview/res-sdl/toolopen.BMP | Bin 3126 -> 3126 bytes indra/newview/res-sdl/toolopen_old.BMP | Bin 0 -> 3126 bytes indra/newview/res-sdl/toolsit.BMP | Bin 3126 -> 3126 bytes indra/newview/res-sdl/toolsit_old.BMP | Bin 0 -> 3126 bytes indra/newview/res/lltoolpathfinding.cur | Bin 0 -> 2238 bytes .../newview/res/lltoolpathfindingpathend.cur | Bin 0 -> 2238 bytes .../res/lltoolpathfindingpathendadd.cur | Bin 0 -> 2238 bytes .../res/lltoolpathfindingpathstart.cur | Bin 0 -> 2238 bytes .../res/lltoolpathfindingpathstartadd.cur | Bin 0 -> 2238 bytes 21 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 indra/newview/cursors_mac/UI_CURSOR_PATHFINDING.tif create mode 100644 indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END.tif create mode 100644 indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END_ADD.tif create mode 100644 indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_START.tif create mode 100644 indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_START_ADD.tif create mode 100644 indra/newview/res-sdl/lltoolpathfinding.BMP create mode 100644 indra/newview/res-sdl/lltoolpathfindingpathend.BMP create mode 100644 indra/newview/res-sdl/lltoolpathfindingpathendadd.BMP create mode 100644 indra/newview/res-sdl/lltoolpathfindingpathstart.BMP create mode 100644 indra/newview/res-sdl/lltoolpathfindingpathstartadd.BMP create mode 100644 indra/newview/res-sdl/toolbuy_old.BMP create mode 100644 indra/newview/res-sdl/toolopen_old.BMP create mode 100644 indra/newview/res-sdl/toolsit_old.BMP create mode 100644 indra/newview/res/lltoolpathfinding.cur create mode 100644 indra/newview/res/lltoolpathfindingpathend.cur create mode 100644 indra/newview/res/lltoolpathfindingpathendadd.cur create mode 100644 indra/newview/res/lltoolpathfindingpathstart.cur create mode 100644 indra/newview/res/lltoolpathfindingpathstartadd.cur diff --git a/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING.tif b/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING.tif new file mode 100644 index 0000000000000000000000000000000000000000..ba6f30fa0e8b8cc8abfc5c2870ebc8bdbcf9d4e2 GIT binary patch literal 504 zcmebD)MAigWMF7uPzbP45#wcVaY_u>kzyvx-{F!JXt5+rSG>n9A^*qYKg{wB$CdG>hk#rOtxDWD(0!ty1^d zmluhgnri#sV^L_^$pW z(ieffeyhWmUrF71^G?}e<;AOGi={K$^2?J~KQy0~d(6D`@czPF8`*!(=8lKbm)weT z*A}dK61}N%-tqM1Ro5?8?|qSOH~09bs@k~y>D%wV-|XPPAi(gCfrSAWG!VcDVJiT6 zj7V%IMrNQY28IVfh0H)UNIfeMvq9NRK(-)MJqM623Ka+G5koRZ9Lg318X(2U3O45j pkSz^me*m&&pn5@eXdszyi^L8>Vgr>ia6lnLIU~ahW}pC+1^~Q>hYbJ# literal 0 HcmV?d00001 diff --git a/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END.tif b/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END.tif new file mode 100644 index 0000000000000000000000000000000000000000..830d5692fd3f1c77820ada0d47399ca46d098187 GIT binary patch literal 556 zcmebD)MD^sWMF7uPzbP45#wcVaY_u>kzyvx-{F!JXt5+rSG>nf{^Rx^Y&MKNCsKke zLW->A7~B~If3o=ba!m773zRgeSZ(dXd7f*_k1t@o|RLmwCC4CfBpJpHh=x`yAQWsFW*rYUjO8( z9q;9&-`3|tSbiPox!m{cK!Mkhm)r7UWH}E_Sfk5*Sm2CFyMh9P0K-2976xE&LI5L# ztpMaPBC(kmnSrVp7(|#D7?^=p@C$+EfPBji49c7zyXB}<%|q3n1KRN8UWSvqT>Jn literal 0 HcmV?d00001 diff --git a/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END_ADD.tif b/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_END_ADD.tif new file mode 100644 index 0000000000000000000000000000000000000000..e05284214a78b096c3186e4f81ac25b4904db2bc GIT binary patch literal 570 zcmebD)MAKXWMF7uPzbP45#wcVaY_u>kzyvx-{F!JXt5+rSG>nf{^Rx^Y&MKNCsKke zLW->A7~B~If3o=ba!m773zRgem_mky>$7h2*YW|v> zXC9S&@!lh@uXOS5>H^;yspqSAo%ygu_NcOX)141VyQTfkv2T4?l(Os2v&FqkPl|R$ z-n9EZz3$nU-_iTd{n~&0-M5{w=h!xHeEPAfXybmq^4PauV{7K-m%Bf{{HS=(fB*g7 zpPEelU$1+=U26#^gZcZ%e@-6o+9J%hkpK5b_Co^F5AHfmSZ#1^r~VdG{v(1su7XD< z>iD!SP!M4F$H2k>3|t6cgs>HWJVqoo6C*QF6$67369WS?kPTAL3dC$sHc*&B5UQR7 z$QFf)gY<|YnIjHmhX4(bVq^uIqXpC~4P_ev*)mYQAUiaW%(q2i2O+V6${0AHkfEHB L;RQ2L07?S@)4{N9 literal 0 HcmV?d00001 diff --git a/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_START.tif b/indra/newview/cursors_mac/UI_CURSOR_PATHFINDING_START.tif new file mode 100644 index 0000000000000000000000000000000000000000..c4822adf6400cf4e2939d9d78ecbcaf66074b62a GIT binary patch literal 532 zcmebD)M7AWWMF7uPzbP45#wcVaY_u>kzyvx-{F!JXt5+rSG>nf{^Rx^Y&MKNCsKke zLW->A7~B~If3i%9)$cmF_(NpKrbP4hlS_&%T&`7GEbvO6{EJ7bR%6kLw2&?zvwq!W z=Q2u8^-MF@UKE;Gva3af*{b_m!jxZEymoSKa(VO3YR|3HB3low{%W%ITp$eM-gX!N#+A1Zd=wOU_yH0jljsH5dF|C=6c3j34e^l|pN_=4-v+w9n9 zT}b&78PQiCTzT%1u_UkBzR6QgoG-MF+!vfVZP}ZQ+xu8{m4rPya5b{m?cU3bM?MyR zW`yf~uD|)==j_=gt0yYHF1Nh9_eu8ND;+;-w#b(3U;f}z)}DEt$M;{pdGK?YT;2Nf zIsaGq8XJ9d5YXXeJ}AJ%B*5^GfrSAWY!JW*VJiT6j7V%IMrNQY28KUCh0H)UNIfeM zvq9NRK(-)MJqM623Ka+G5koRZ9Lm-M8X(2U3O0uYs974y<^i&0pn5@eXdszyi^L8> VVgr>ia6lnLIU~ahW}pC+1^{i1p7Hkzyvx-{F!JXt5+rSG>nf{^Rx^Y&MKNCsKke zLW->A7~B~If3i%9)$cmF_(NpKrbP4hlS_&%T&`7GEbvO6{EJ7bR%6kLw2&?zvwq!W z=Q2u8^-MF@UKE;Gva3af*{b_m!jxZEymoSKa(VO3YR|3HB3low{%W%ITp$eM-gX!N#+A1Zd=wOU_yH0jljsH5dF|C=6c3j34e^l|pN_=4-v+w9n9 zT}b&78PQiCTzT%1u_UkBzR6QgoG-MF+!vfVZP}ZQ+xu8{m4rPya5b{m?cU3bM?MyR z_+q#xvc0^{&nv~ zoA$lX{`AJWM|#!`+x3U9{M&ZH+2_n&fgDxNLlf5MFeoSpF#Ka+VE_gs1TaF_3P2ts z5}S#U8K{bZfscuSff>jKsb>XZHYgh?%peF=&jDnMLd8LP#E{Gphq4`j21qfog3S>F oYLC&4K@Y&rtr@r#yw=oMvB{5gzpvO z@)?J4#*FN73Sl0rJ^!|01zrW_ZXfLR6{P+M^OY#%qqeU^IlrX@sw7MbhcKT|i7YU= zM0dV~yCpK42RVXwK53+)H1VOqsJeo8FVV`!b)2_|^-trXv*pgiLyDE^3Y?OY_xLzSd_~A{$M>^X Wg7^x~w?N!XEn=l$JweiT?tcKn=N23Q literal 0 HcmV?d00001 diff --git a/indra/newview/res-sdl/lltoolpathfindingpathendadd.BMP b/indra/newview/res-sdl/lltoolpathfindingpathendadd.BMP new file mode 100644 index 0000000000000000000000000000000000000000..fa19f3f1058dbe586920d7c42c9c1891b8d2ed46 GIT binary patch literal 3126 zcmeH_F%rTs3D>z4qxRHfpJI+iBG$fp5c~4PtyAQ`O#(?`3 z*JZj6;S445JcSUiIbQ$R>jd5f(cC-O^Cyt|M_HRiAs^mt7Uldk7I4V~4-RE*gUXx& zQ!Tpl8Qwf1w|S5ayd&QwBk6Whm5fc(z-98tR6*S05&V6XO6~<0Am4kD!C@@4Jd697 zRmn}sF1)I}U&SsNNv=0b-dfa#pQ5B7&4Y}jM8*XL`a+@8N-Z$EG5?r;W{V+uTwJhB zBW(aR0%yTk*0H8I#GN~5%~;m87GRo?3^R=`v=A)!%-S%`(zK*Y9Ud}r1VXSZ46Se( e31cYSSS(H^ZzOyJR}19btvu80t|u7ao|hj4<|PdP literal 0 HcmV?d00001 diff --git a/indra/newview/res-sdl/lltoolpathfindingpathstart.BMP b/indra/newview/res-sdl/lltoolpathfindingpathstart.BMP new file mode 100644 index 0000000000000000000000000000000000000000..912b7f931a744d3cf4e775aba5198ba0518e42a9 GIT binary patch literal 3126 zcmds#K@I{T3`KEoT)TAX3FZLqU3y~|-bz2yp=l`4Is+zSBf>!Y{-@z~pN?UU3IA7& z%Q6n(j1|T66ha&+UVjwt6eI2gc6cm5touqxIH{zo7_-NQhO?SxK|nb>j0r>Dx3Lmni&BR{iE&+G*3PNfZdW-^0nfUvZDYqt(`gxBqbNb|YgBPZ@{15oD4rIXa+Axc zOZe2m4aAYGb*@px1aq->iFN*|lc+5QHw~xGp8M0lB>o6EY!H7Cocrd#@7^;%=Xo32 literal 0 HcmV?d00001 diff --git a/indra/newview/res-sdl/lltoolpathfindingpathstartadd.BMP b/indra/newview/res-sdl/lltoolpathfindingpathstartadd.BMP new file mode 100644 index 0000000000000000000000000000000000000000..4e8999ae0bff6c34b7b442bcc457d4803f7fb64f GIT binary patch literal 3126 zcmds$F%H5o3`N6^m>C&4K@Y&r$c-7e75w7%D~*%bsZyk35ipMZ{B7v%J{-f>2E1Q! zF4H-LGd!{9DTMLN_WC1x&$h)(V28)_g5S3V;jshXG>;5GV&w~e{u`7&QERpkej*iJ z#<;_fR&OzKqRlUS2OlZ^7VHJbw75Bvo2IBI%Pp8}Ve(Nz!L4x5iabopj+M&@9{rvc zS#lRRATTbxqwEqHanH#09Id(6ulZxy(yl(N=e8ui;oR1G8iSZFbTocR)sz@-My|-AWUv`MvtZ`|$HMiZG0H*M_fJ2P% NH-d9-tyAwe{{y$5D$4)> literal 0 HcmV?d00001 diff --git a/indra/newview/res-sdl/toolbuy.BMP b/indra/newview/res-sdl/toolbuy.BMP index a00bdb2c58d3a4650eb93999d6252cede3028185..07e927372182c096659347c786dcd83f86dbfa85 100644 GIT binary patch literal 3126 zcmd6pJ4jnm7=V+Y7)*TyAMsV+HX`v=4Hd1Ah;J*1pdum^goG>(ZsI12gG+G}+#IBX zjt(xxXD4;&6r#Is3Z3m|{^oFd@6ByM3;iJ{|2^m2?|+_?1U~uS*ka;m^zteH{ju9T zXb9_WmwaTxX8ZmhOWeoj?|&Mg6>guYsiK?oq#>U3Fy1L5B%C|AWRCRTAK|uk8NF)MWMk627)FlxgAmrlW;_U2f zMwstnf>Kmeq~JISL9Ea?FxAK;MosxOH8nsW*VfjywzjslwY`rEWK=T4>*5|W=%!-I zhgn!yNFKD{D8w}~GQ!BJ?x1-*9@6gj`?XA+&RYmp znwpwWa9)5UN0G@4CUo%p{CqsOhAUg}8yXrEA0RqGfi;rFP-bRkqO>TiC@U)~KA%s= zH?5`$!R>ZSK_QHej&5yjefd)c1_tnXW@d(^o12@>&CSKdMXmn|Wvb-l78ZUQ zWOjD;^z@YJy}i9?G`hXLeS3RLDkv*yw{{+jw2exR@UO0}=H});$ECBgb9#E3&LN7k zvop@!+S=OP-5n-?bFNL@R+AMK6?D6VApGOwV<0LhtpdNVukZT$8t2Ex#%LtE&Th9W znohw1lCKCB{O<1Vjg1Z5CQ)Q56bfX|y&*-yuhW_vB>LLW335UbtKZWB_ z$>8AN!NCD3Vih?&JfxjSQEzW=Zf>rYX4KQt(x3$b0Za@H4Pk=jBpe=%vb(#B+cMwN z(?iF)TrMM1H^cqIAtl(4Nei0^inF6bnLg40RBd>8n77E)UxjT%$`wKEra y3OWLoM<%%(_@!_FY_}PR?{t*ARRQ(OgHTWDSr%0@>@}rU<(2`(& z#F$^gnKe1WeDuBd<|wUKUh%JQ$@)?ptHkmItM~x%Fcqb?b-RwvD4C3^yx@+}=xCs0PfVQgF_9CW zDV{Y(%~#{j9^p+0=4rjM!<8 diff --git a/indra/newview/res-sdl/toolbuy_old.BMP b/indra/newview/res-sdl/toolbuy_old.BMP new file mode 100644 index 0000000000000000000000000000000000000000..a00bdb2c58d3a4650eb93999d6252cede3028185 GIT binary patch literal 3126 zcmdUtF;W9D3`NZhUFc{iDLFw7K+nA?xD))zOa5qN#dsOJK$BHu>FY_}PR?{t*ARRQ(OgHTWDSr%0@>@}rU<(2`(& z#F$^gnKe1WeDuBd<|wUKUh%JQ$@)?ptHkmItM~x%Fcqb?b-RwvD4C3^yx@+}=xCs0PfVQgF_9CW zDV{Y(%~#{j9^p+0=4rjM!<8 literal 0 HcmV?d00001 diff --git a/indra/newview/res-sdl/toolopen.BMP b/indra/newview/res-sdl/toolopen.BMP index 93c7e44b28d960eb0c67de79a5c6e3846dbdf0b2..5b87979304979a70dd091f04df1977261bd3ef5a 100644 GIT binary patch literal 3126 zcmd6pIZslRY%o{2jALYR2CQ&|!5X&(?gFe@dN1K7f_~51C@mY z1(ju_(Aj_p6y(DbuQvN04CkN;T8F+P6ZBh1m!QQd{;Z{-MDI-UMrmV<+X{r&yj-Q73Y3lM^bT__YDN`fNt zdcFbQ+}wn$+P%HKSGv$6KR!ODtG7o-M=#`eY0}x*+1}nB2m~rBDo8s4OImcY=-G#d zjg1Xw7rni`LqkLT{rx>XJs(Sgt57J^+}upGXJ==?rM2>LeRz2I6dxeuwzjs0h6Y-g zpGtz#+S;n%1PMXBP&qJFixH=`{Jy?EAdti1@YvYc`1ttevOq>9Gc2cUSdInVR&4n& zTUuJkgN`lZAHM)cMn-VK%#Mx@;S>4x_O=`c3j11JU8QBzxM;y(khI5QF`a4XtU~ZI zJUom-@B$<`ipo6X+d-d_H^BzSQFoO^AJS+{g|cT??Z3%~3{@(=&|`WhEZ#)Sw~=XSew zpso`D$yada_{l$fM4umtM5?N)bf&Juk4B>t6BA!vAm``jcXxL;H#hp+fq{X_%1WJS z=sXM4)6=+kiRANnT&S5-Q&WWJe8jwCv$L~m3CiT;q;yp+>7%$%T^4eDM!-gw`k$GZ zVG9Ifh~gV|)t#T8_xt?@(CAiISM#34<8fSwBoYZx6u&(3*LQAijwNhQ}`xh=e1kudla7J!QlN55@ZWI`IP`OGqx4 z6Gd8vm_YsTT7q(8nct53iNHh8v@HVOE zB(Qjj8RMt4^h=1pg@Xje+>>cHHZzhxFGIHk_%dJXvxJv$^N$i##c4`qK7~XMM-j0W zpFuwJ3V3G;Wj^#-Sp`PK{5_vEn}3ynwYXD7!@pMCJ=@YV9IdbWk19_2E$75KTw6%_ zxPO&!#z&YIOQ^)a^LL5tf3YRtF0vBF@r+*~OM)wFdsRX<8UC44Cx7Q3w>O}}xysuA zW?oB-UDW5l9+B(b7TWzof+~G{ahxXe3eCuzxBX+wjeHjQKj-ThL8|4o)8eCz{~69r zLj1Lr58x{s2DWhad&$m2g635MyyI#3R|&QXoqYP-6tbIPKHNXcY9-{W&WGo(Z*z|U z4D??~`A0r;&Ig?GSAg3Hhj1%=u;Zn5#ZxmggYOc!1sOmzNnj9g&1~U99sBTL5~jk4 zg0D2Bwd2=RT7{JJc2<{sNmSvIK)mvH?C(4(Jqs=&W_H297=oi0_Ji#>#?0;!X7t_2 zJop+M;llRxQBl=D_$A!UTfF=`o_pL*(-ixv9COn0tnzp8k{`5kP?9y)zZ3W(+Ag0e zJlnQIfaY-|@aMo|joZd&OqXYc;5+^iju#ksx%Vn#$(cHvl95FCZ^HGJvp%2FTl3++ o4bP{2afCuCj?)T7q(8nct53iNHh8v@HVOE zB(Qjj8RMt4^h=1pg@Xje+>>cHHZzhxFGIHk_%dJXvxJv$^N$i##c4`qK7~XMM-j0W zpFuwJ3V3G;Wj^#-Sp`PK{5_vEn}3ynwYXD7!@pMCJ=@YV9IdbWk19_2E$75KTw6%_ zxPO&!#z&YIOQ^)a^LL5tf3YRtF0vBF@r+*~OM)wFdsRX<8UC44Cx7Q3w>O}}xysuA zW?oB-UDW5l9+B(b7TWzof+~G{ahxXe3eCuzxBX+wjeHjQKj-ThL8|4o)8eCz{~69r zLj1Lr58x{s2DWhad&$m2g635MyyI#3R|&QXoqYP-6tbIPKHNXcY9-{W&WGo(Z*z|U z4D??~`A0r;&Ig?GSAg3Hhj1%=u;Zn5#ZxmggYOc!1sOmzNnj9g&1~U99sBTL5~jk4 zg0D2Bwd2=RT7{JJc2<{sNmSvIK)mvH?C(4(Jqs=&W_H297=oi0_Ji#>#?0;!X7t_2 zJop+M;llRxQBl=D_$A!UTfF=`o_pL*(-ixv9COn0tnzp8k{`5kP?9y)zZ3W(+Ag0e zJlnQIfaY-|@aMo|joZd&OqXYc;5+^iju#ksx%Vn#$(cHvl95FCZ^HGJvp%2FTl3++ o4bP{2afCuCj?)bOeXhSZb-CAC%K3JJ!kH3=FIuf##!l;Z(t?bEYI5C$L~v^FveqH^Oz2w@8^GP z!yMm!|3#X6dwV*B(H|8E+Qr4ikD>K?z1!Q{>+9Q^g1vE_W1bte!c^K zd3gz0+MAo3cZSfJ-{0T!>S)L0uPIwyU0q&Yo}Zten3$mKBy7s!AQS7?>+99km3N5s z_4Rx{zqPfMPNzQ)j8GdJ8w(2y5FQ>LfScCl6Pk4y#RmvEnM}^k&eFpCGBCu_(vrYQ z5`u}s&VeZ{odk8|Z*Fb^ft<-?_V@RT#p2hYK}J#;J~!#ngLXAnKFr0%Me3ljL;Tw> zz}?+lLg2ZwvSRpTPPNT)Aeh(j@i8r|<4 zLZ&i=aKPj7IGGDvGl2--goD4cvm<Mh+bv0!Qu~^J3 zsN|`cS__{&rt=q+tB6LULN-D(wH7`lq7*DEW%o{=h-@2O90QSvHlHDqHz9y?u3giX z&QvPJZWo~8gLT-;%kO@_Pp9!DgfVuV-|rWiLy!Q)*YHt%3FmOmX0w4nz=4%l__94dF ztOz~Ihb*6+v+iO4SF2T~Kr*%oR)qFBJUpD6o6|H~2ZKTGlSZRK2-^fJLVW7=I=eI+ z4%I-Da}iEfcH=1nIg0z!FhV+aSf5Ld1TYSa*g;$H>U z`uzO-IRMOG_ literal 3126 zcmcJPF>b>!5Cjzu>N!DfB02_m)1NL|LK*N1%xlP0lWl zNBs~-g7)2Q=gv4n~=Ibfj=2)cu9F2hGBCe zZ0~>zyD5PQfWv=nqwD*6upU{Fz+cL_=0p|5jeID|GA<0NL%_MR96yj#i@{I#37B0#pkGn4gp*u$G%G zYdL`L62SeZjOl0Usi97(A22TFZWLU4B;=Rg7lubPkJEAgv6K0$EHgRCa( z!aURVwP|EKOM+i67X*?J!A+V~(maqHHe#Y162JoP{9^CiF-UC4H#dMjqxETI6B)g! z+?ANY;a>|`^LDc+0j;x1kWYzx<(E0(xIGC<#U1|bs@lzB$(a9s>OXNWx$MP(E>Xj; z{%?GZDAgjU$2M^s9wUE>#gxQ8zO*cjg&E_>jK?Zb>!5Cjzu>N!DfB02_m)1NL|LK*N1%xlP0lWl zNBs~-g7)2Q=gv4n~=Ibfj=2)cu9F2hGBCe zZ0~>zyD5PQfWv=nqwD*6upU{Fz+cL_=0p|5jeID|GA<0NL%_MR96yj#i@{I#37B0#pkGn4gp*u$G%G zYdL`L62SeZjOl0Usi97(A22TFZWLU4B;=Rg7lubPkJEAgv6K0$EHgRCa( z!aURVwP|EKOM+i67X*?J!A+V~(maqHHe#Y162JoP{9^CiF-UC4H#dMjqxETI6B)g! z+?ANY;a>|`^LDc+0j;x1kWYzx<(E0(xIGC<#U1|bs@lzB$(a9s>OXNWx$MP(E>Xj; z{%?GZDAgjU$2M^s9wUE>#gxQ8zO*cjg&E_>jK?Z{M!^ zoe*tw$5*NNNYrXGYGDXSh*GG9;|nDcfB#vQon6>PIxo4qGxwhF+;eAU2VfausI3Lo z>hF3LumQk!bq%3aJq8V`bysG;WsLufL07esG9waP>aLyC% zAmKeE{1GDS!Ze!`z)txVc}u=S{*-*L{3ZDz`8)EDIfa)~xM*gk*JNXatpXH|te4<|4->vPpYx|wre!sSVL)(7> z@TCF1F~KnleD8oCT`=&#DL-F-qDEeq&Tf!zmftV0T)|2CE(7c{!K)T{%K;y`%ISfV ze&j3U^=#V9DM=X{ZSu$DPs{hqUz5Kh|3Lnkyw-F_N?_9??jW$00cL1lQV3=_%_7{phT_-+wX4)N0^raj`fA3eWYcwdEYv<0nrsQ4@lo;Nb@Gz^GZiTH3R-$J}FLD6*i`Pi&1=X0}V zrWeO^v*qw?h2>`dv2qsYR>*I4?qF`^x$}A7g{ey|pB>8umAZ1_RL3l$}#P;%&;lZN1C~_$$`X650hAYVKn7UrC-8jqP z$`kxCqYPLKq;Y+!3aun6>(fJ^|R zpD@25EQfGT6YebG%@h8;stMC<%>p}ez@WS(KPrDz{-per{G9v+`K$6J`TO#Z6Fwuq zPySo^$#2gAJ8hu901Ov_L-OAQz%d#) zB|k0yv;42}x8;`-zE6HA2OPG6BL!fh2%M9@7C?0xxGVpM{9p19<)6r}Bz(3{+y6}4 zAJO)YX!{e|{yA;`I>6foc-I8)Ti}udK61gp16TZ7{n;*gT{?TG{DAy^dF2YmV^rC=IQN%8k^yCNF0~&`x(*~{Mpfe4+=Rxl#=*Rajt2ZSlV`EtUYx!gH6Y^)| zFUVh!zag(R-39q&`S|(CAd7v}7A53j&S^4sKl<@@9_@&g8O z(A2wQ5kEM}P{+w`l0%>a7M#O!)Z7nZfndBhiWav zZziYI${3MhDT#(RssnGWA-^m&dA;qARD{ zQOgn@RnPQQK1!!jsp?QPQn`VZsbRQrLM@%{bvgW&361e+`AUvAy=thoHP=rp9aWnw zFAi&+Xrqb!C*n~!IrJ{a`oCY_@@@Jh(4w9Se-V)R_V39+{egO3U1P5m4>a#Vw}u95 pL#u7`T>Ywha&>TH+v-qj%WM7g_*Y$dQp~psZ4~OBB7ip9LMqBZb@6w6o=H8z@aUN#>6GdT=3Kvk4jB;OpHoROzc6q?IeUyFYXBl znznNi6S&z%F1icokqVfIr_CS-LW)A92j#%{R!SiL{m$;r&TQ#wJo&Uw=b8WWeE!e# z%u6~_UO5d9OHcCiS-XUacKU@W!kTq%uE_AOFI^TRcW*?6(Tuf zCiiud=bQWwO~In6wrmQow@jR7M^V~ck|yvTeg=OCe++*Dzl1-FzksjfZ{Y7_d;z}? z{~rDbei45Oe+z#X|117?{Ga%H`1|+=8DAWcN-s#|DQRIws?JLFc_~_wW9Q`Bd3knO zzJ0I2R#dZ9g%&mU9IwAPfPWUh3tzzR#ZTg2#!usY{89WV{2Bar_#fLo&+@mh{9cyd z&+@mj{Kr}TK*XiA?HV&z)8=WGJSQyBmsZnN3)_AWzpEteDNADu(o|J?6aQ``9X3bD z@h9=0=5t6}PU@I0I4Ui@SDt@tPK+!h<&E*p1cZ+Wu!ec8u>?5mpWYM6Qr zsHA69)`%ALysFx(I!}J2mt@X)RBluB#0YC1T z-f($$Jkv)$xdPL2&2&9Xef$di?AQJIt1yDu zHq4*N+~{r$$YjsM2~R_Xu1ETZ0|R;a`t x(jPJI<`AKYc4)nC+1B44nqQyT*0(;{+w+i5kH4to$yucrv<);gZ?nGE$Fl&^j~{D{C&C`fx-gI}-7zM4|`WW)cx~X-^1JJ0~RE zwBf>z*HOI^oHm1lNKv5&985i`LVSH^pVM}?g#YHhGxPubKfnKDW_E!%VrXmx=Ie8@ z4p;SB0kv!q}Bm;2axUpGTlJ-6c7x+;B}b11&a^i@CjTF!Q%*g2KhCI z^H0{xFOgp%pO9ZG-!8vVzEj?jKPrD-{;K>V`Bx<$*Ysy-`ZG2CI!%AJrawp1uMf19 zT!0pu=%j^i5()VpI5CmrQ-i9wHe z?w5V|3j9xr$K%uScq|s1rb8H3hiHjr%ZDdsh-Rx4*DqqJRE$sUFnRb%86ua+;b_C8 z_2)v-cxf)-Nnx+5#q;geHgtMij8@5AXs#Ch!^pWs{M)<=yPe0ChR?MX#*f~b;>Lxc zg43aJRty$ng*#ZhjD)9q6~~{Dxson*h*wx%DHxtit0(mQs|)?_ z=gO$QhgEN*mc6J^jj2M{bv?rjdPX_iL-wlbXZL3f8Pp0aS5sT5X}orjEka47QlcK|MIw7BQIq`=8x)XBb#G_`wr$FZ-=w1iCDbSDZUsi8QPR2&B{EzY{ChFsYAz&Lo*D9<~TD%4y)^H_hTIy rGzhe4sJl9}&@jn`A6mH3y@?Aw4g9D)|0xf(<*?>+le!Ei)ywUFD=FXi literal 0 HcmV?d00001 From c181891549e90aebd067bf1cdc9ad1eea50be377 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 4 Oct 2012 18:28:51 -0400 Subject: [PATCH 122/123] New windlight presets --- ...20smoky%20blue%20sky%20%253A%20reverse.xml | 141 ++++++++++++++++++ .../windlight/skies/Ambient%20Dark.xml | 141 ++++++++++++++++++ .../windlight/skies/Ambient%20Grey.xml | 141 ++++++++++++++++++ .../windlight/skies/Ambient%20White.xml | 141 ++++++++++++++++++ .../Annan%20Adored%20Dark%20Red%20sky.xml | 141 ++++++++++++++++++ .../skies/Annan%20Adored%20Darkness.xml | 141 ++++++++++++++++++ .../skies/Annan%20Adored%20Dusty.xml | 141 ++++++++++++++++++ ...nnan%20Adored%20Light%20Explosion%20II.xml | 141 ++++++++++++++++++ ...nan%20Adored%20Light%20Explosion%20III.xml | 141 ++++++++++++++++++ .../Annan%20Adored%20Light%20Explosion.xml | 141 ++++++++++++++++++ ...%20Optimal%20Skin%20%28no%20shadows%29.xml | 141 ++++++++++++++++++ .../Annan%20Adored%20Realistic%20ambient.xml | 141 ++++++++++++++++++ .../skies/Annan%20Adored%20Red%20moments.xml | 141 ++++++++++++++++++ .../skies/Annan%20Adored%20Tan%20Skin.xml | 141 ++++++++++++++++++ .../windlight/skies/Gwen%27s%20Light.xml | 141 ++++++++++++++++++ .../skies/Oceane%27s%20Body%20Designs.xml | 141 ++++++++++++++++++ .../windlight/skies/Tron%20Legacy%20clean.xml | 141 ++++++++++++++++++ .../skies/Tron%20Legacy%20extreme.xml | 141 ++++++++++++++++++ .../windlight/skies/Tron%20Legacy%20hard.xml | 141 ++++++++++++++++++ .../windlight/skies/Tron%20Legacy%20soft.xml | 141 ++++++++++++++++++ .../windlight/skies/wastelands.xml | 2 +- 21 files changed, 2821 insertions(+), 1 deletion(-) create mode 100755 indra/newview/app_settings/windlight/skies/%5BEUPHORIA%5D%20smoky%20blue%20sky%20%253A%20reverse.xml create mode 100644 indra/newview/app_settings/windlight/skies/Ambient%20Dark.xml create mode 100644 indra/newview/app_settings/windlight/skies/Ambient%20Grey.xml create mode 100644 indra/newview/app_settings/windlight/skies/Ambient%20White.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dark%20Red%20sky.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Darkness.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dusty.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20II.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20III.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Optimal%20Skin%20%28no%20shadows%29.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Realistic%20ambient.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Red%20moments.xml create mode 100644 indra/newview/app_settings/windlight/skies/Annan%20Adored%20Tan%20Skin.xml create mode 100644 indra/newview/app_settings/windlight/skies/Gwen%27s%20Light.xml create mode 100644 indra/newview/app_settings/windlight/skies/Oceane%27s%20Body%20Designs.xml create mode 100644 indra/newview/app_settings/windlight/skies/Tron%20Legacy%20clean.xml create mode 100644 indra/newview/app_settings/windlight/skies/Tron%20Legacy%20extreme.xml create mode 100644 indra/newview/app_settings/windlight/skies/Tron%20Legacy%20hard.xml create mode 100644 indra/newview/app_settings/windlight/skies/Tron%20Legacy%20soft.xml diff --git a/indra/newview/app_settings/windlight/skies/%5BEUPHORIA%5D%20smoky%20blue%20sky%20%253A%20reverse.xml b/indra/newview/app_settings/windlight/skies/%5BEUPHORIA%5D%20smoky%20blue%20sky%20%253A%20reverse.xml new file mode 100755 index 000000000..4d50f8091 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/%5BEUPHORIA%5D%20smoky%20blue%20sky%20%253A%20reverse.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.8300000429153442 + 1.8300000429153442 + 1.8300000429153442 + 0.61000001430511475 + + blue_density + + 0.21899415552616119 + 0.40148928761482239 + 0.68000000715255737 + 0.34000000357627869 + + blue_horizon + + 0 + 0.10838708281517029 + 0.14000000059604645 + 0.070000000298023224 + + cloud_color + + 0 + 0 + 0 + 0 + + cloud_pos_density1 + + 0.14000000059604645 + 0.62000000476837158 + 0.99999999999999767 + 1 + + cloud_pos_density2 + + 0.48999997973442078 + 0.19999998807907104 + 0.125 + 1 + + cloud_scale + + 0.43999999761581421 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.199999853917745 + 10.010999722693327 + + cloud_shadow + + 0.29999998211860657 + 0 + 0 + 1 + + density_multiplier + + 0.00015999999595806003 + 0 + 0 + 1 + + distance_multiplier + + 10.800000190734863 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 0.94999998807907104 + 0 + 0 + 1 + + glow + + 4.9999999403953552 + 0.0010000000616982377 + -0.47999998693999579 + 1 + + haze_density + + 0.94999998807907104 + 0 + 0 + 1 + + haze_horizon + + 0.25999999046325684 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0 + 0.96692299842834473 + -0.25506836175918579 + 0 + + max_y + + 1128 + 0 + 0 + 1 + + preset_num + 28 + star_brightness + 0 + sun_angle + 1.8287147283554077 + sunlight_color + + 0.65999996662139893 + 0.78157895803451538 + 0.89999997615814209 + 0.29999998211860657 + + + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Dark.xml b/indra/newview/app_settings/windlight/skies/Ambient%20Dark.xml new file mode 100644 index 000000000..20aa5ba44 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Ambient%20Dark.xml @@ -0,0 +1,141 @@ + + + ambient + + 0 + 0 + 0 + 0 + + blue_density + + 0 + 0 + 0 + 0 + + blue_horizon + + 0 + 0 + 0 + 0 + + cloud_color + + 0 + 0 + 0 + 0 + + cloud_pos_density1 + + 0.5 + 0.5 + 1 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.41999998688697815 + 0 + 0 + 1 + + cloud_scroll_rate + + 11.539999961853027 + 10.01099967956543 + + cloud_shadow + + 0 + 0 + 0 + 1 + + density_multiplier + + 0.00042999998549930751 + 0 + 0 + 1 + + distance_multiplier + + 8.1000003814697266 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.079999998211860657 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + -0 + 0.89100658893585205 + 0.45399042963981628 + 0 + + max_y + + 718.70001220703125 + 0 + 0 + 1 + + preset_num + 23 + star_brightness + 0 + sun_angle + 1.0995575189590454 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20Grey.xml b/indra/newview/app_settings/windlight/skies/Ambient%20Grey.xml new file mode 100644 index 000000000..6949d336b --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Ambient%20Grey.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.9499999284744263 + 1.9499999284744263 + 1.9499999284744263 + 0.64999997615814209 + + blue_density + + 0 + 0 + 0 + 0 + + blue_horizon + + 0 + 0 + 0 + 0 + + cloud_color + + 0 + 0 + 0 + 0 + + cloud_pos_density1 + + 1.6884100437164307 + 0.52609699964523315 + 0.99999999999999289 + 1 + + cloud_pos_density2 + + 1.6884100437164307 + 0.52609699964523315 + 0.125 + 1 + + cloud_scale + + 0.4199999868869746 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.069999694824219 + 11.050000190734863 + + cloud_shadow + + 0 + 0 + 0 + 1 + + density_multiplier + + 0.00089999998454004526 + 0 + 0 + 1 + + distance_multiplier + + 0.80000001192093606 + 0 + 0 + 1 + + east_angle + 4.7123889923095703 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000474974513 + -0.47999998927116394 + 1 + + haze_density + + 0.029999999329447746 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + -0.99144464731216431 + 0.13052797317504883 + -1.182285913614578e-008 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 3.0106911659240723 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/Ambient%20White.xml b/indra/newview/app_settings/windlight/skies/Ambient%20White.xml new file mode 100644 index 000000000..c30c1db26 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Ambient%20White.xml @@ -0,0 +1,141 @@ + + + ambient + + 3 + 3 + 3 + 1 + + blue_density + + 0 + 0 + 0 + 0 + + blue_horizon + + 0 + 0 + 0 + 0 + + cloud_color + + 0 + 0 + 0 + 0 + + cloud_pos_density1 + + 1.6884100437164307 + 0.52609699964523315 + 0.99999999999999289 + 1 + + cloud_pos_density2 + + 1.6884100437164307 + 0.52609699964523315 + 0.125 + 1 + + cloud_scale + + 0.4199999868869746 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.069999694824219 + 11.050000190734863 + + cloud_shadow + + 0 + 0 + 0 + 1 + + density_multiplier + + 0.00089999998454004526 + 0 + 0 + 1 + + distance_multiplier + + 0.80000001192093606 + 0 + 0 + 1 + + east_angle + 4.7123889923095703 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000474974513 + -0.47999998927116394 + 1 + + haze_density + + 0.029999999329447746 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + -0.99144464731216431 + 0.13052797317504883 + -1.182285913614578e-008 + 0 + + max_y + + 4000 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 3.0106911659240723 + sunlight_color + + 0 + 0 + 0 + 0 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dark%20Red%20sky.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dark%20Red%20sky.xml new file mode 100644 index 000000000..f3005fbea --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dark%20Red%20sky.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.3079376220703125 + 0.31744983792304993 + 0.4186248779296875 + 0.1395416259765625 + + blue_density + + 0.078125 + 0.078125 + 0.078125 + 0.0390625 + + blue_horizon + + 0.1856689453125 + 0.2917042076587677 + 0.3768310546875 + 0.18841552734375 + + cloud_color + + 0.2981414794921875 + 0.0065460205078125 + 0.081723079085350037 + 0.2981414794921875 + + cloud_pos_density1 + + 0.68000000715255737 + 0.5 + 1 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.18000000715255737 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.49940013885498 + 10.01099967956543 + + cloud_shadow + + 0.47999998927116394 + 0 + 0 + 1 + + density_multiplier + + 0 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 0.25132742524147034 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.0199999809265137 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000474974513 + -0.33000001311302185 + 1 + + haze_density + + 0.57999998331069946 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.22304299473762512 + 0.44229033589363098 + -0.86869502067565918 + 0 + + max_y + + 880 + 0 + 0 + 1 + + preset_num + 21 + star_brightness + 0 + sun_angle + 2.6834418773651123 + sunlight_color + + 1.56097412109375 + 1.4059410095214844 + 1.34527587890625 + 0.52032470703125 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Darkness.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Darkness.xml new file mode 100644 index 000000000..c4495ce55 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Darkness.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.21000002324581146 + 0.21000002324581146 + 0.21000002324581146 + 0.070000007748603821 + + blue_density + + 0.51999998092651367 + 0.51999998092651367 + 0.51999998092651367 + 0.25999999046325684 + + blue_horizon + + 0.296875 + 0.296875 + 0.296875 + 0.1484375 + + cloud_color + + 0.69140625 + 0.69140625 + 0.69140625 + 0.69140625 + + cloud_pos_density1 + + 0.57999998331069946 + 0.44999998807907104 + 0.91999995708465576 + 1 + + cloud_pos_density2 + + 1.6884100437164307 + 0.52609699964523315 + 0.125 + 1 + + cloud_scale + + 0.41999998688697815 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.649999618530273 + 10.359999656677246 + + cloud_shadow + + 0.47999998927116394 + 0 + 0 + 1 + + density_multiplier + + 0.00014000000373926014 + 0 + 0 + 1 + + distance_multiplier + + 11.5 + 0 + 0 + 1 + + east_angle + 3.958406925201416 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1.0499999523162842 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.55000001192092896 + 0 + 0 + 1 + + haze_horizon + + 0.099999994039535522 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + -0.5154578685760498 + 0.70710796117782593 + 0.48404696583747864 + 0 + + max_y + + 886 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 2 + sun_angle + 2.3561928272247314 + sunlight_color + + 0.87000000476837158 + 0.87000000476837158 + 0.87000000476837158 + 0.28999999165534973 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dusty.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dusty.xml new file mode 100644 index 000000000..b3a4a13c9 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Dusty.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.291046142578125 + 0.42025142908096313 + 0.505828857421875 + 0.168609619140625 + + blue_density + + 0.15999999642372131 + 0.15999999642372131 + 0.15999999642372131 + 0.079999998211860657 + + blue_horizon + + 0.19999998807907104 + 0.19999998807907104 + 0.19999998807907104 + 0.099999994039535522 + + cloud_color + + 0.61395263671875 + 0.50241565704345703 + 0.48760986328125 + 0.61395263671875 + + cloud_pos_density1 + + 0.52999997138977051 + 0.45999997854232788 + 0.070000000298023224 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.039999999105930328 + 1 + + cloud_scale + + 0.15999999642372131 + 0 + 0 + 1 + + cloud_scroll_rate + + 20 + 20 + + cloud_shadow + + 0.68000000715255737 + 0 + 0 + 1 + + density_multiplier + + 0.00015999999595806003 + 0 + 0 + 1 + + distance_multiplier + + 12.199999809265137 + 0 + 0 + 1 + + east_angle + 5.026547908782959 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1.2200000286102295 + 0 + 0 + 1 + + glow + + 0.19999980926513672 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 4 + 0 + 0 + 1 + + haze_horizon + + 0.17999999225139618 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + -0.93278193473815918 + 0.19509218633174896 + -0.30307888984680176 + 0 + + max_y + + 347 + 0 + 0 + 1 + + preset_num + 18 + star_brightness + 0 + sun_angle + 2.9452412128448486 + sunlight_color + + 0.91124993562698364 + 0.61839842796325684 + 0.52875006198883057 + 0.30374997854232788 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20II.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20II.xml new file mode 100644 index 000000000..24a192b95 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20II.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.6568816900253296 + 1.5270273685455322 + 1.4837433099746704 + 0.55229389667510986 + + blue_density + + 1.1843417882919312 + 1.3887529373168945 + 1.4597588777542114 + 0.72987943887710571 + + blue_horizon + + 0.468017578125 + 0.62104421854019165 + 0.641357421875 + 0.3206787109375 + + cloud_color + + 0.0234222412109375 + 0.018011331558227539 + 0.0156402587890625 + 0.0234222412109375 + + cloud_pos_density1 + + 0.39999997615814209 + 0.81000000238418579 + 0.10999999940395355 + 1 + + cloud_pos_density2 + + 0.56000000238418579 + 1 + 0 + 1 + + cloud_scale + + 0.08999999612569809 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.6499996185302734 + 10.01099967956543 + + cloud_shadow + + 0.5 + 0 + 0 + 1 + + density_multiplier + + 0.00017999998817685992 + 0 + 0 + 1 + + distance_multiplier + + 2 + 0 + 0 + 1 + + east_angle + 1.2566369771957397 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1.6299999952316284 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000474974513 + -0.39999997615814209 + 1 + + haze_density + + 1.2300000190734863 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.57896542549133301 + 0.79335421323776245 + -0.18811732530593872 + 0 + + max_y + + 293 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.2252933979034424 + sunlight_color + + 1.62725830078125 + 1.8157768249511719 + 1.98211669921875 + 0.66070556640625 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20III.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20III.xml new file mode 100644 index 000000000..ba2df9e6e --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion%20III.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.89040267467498779 + 1.0580335855484009 + 1.4399999380111694 + 0.47999998927116394 + + blue_density + + 0.43015211820602417 + 0.44939243793487549 + 0.51507484912872314 + 0.25753742456436157 + + blue_horizon + + 0.15130999684333801 + 0.30000001192092896 + 0.35131001472473145 + 1 + + cloud_color + + 0.21396400034427643 + 0.21396400034427643 + 0.21396400034427643 + 1 + + cloud_pos_density1 + + 0.099999994039535522 + 0.09771379828453064 + 1 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.079754598438739777 + 1 + + cloud_scale + + 0.41999998688697815 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.644780158996582 + 10.423800468444824 + + cloud_shadow + + 0.45999997854232788 + 0 + 0 + 1 + + density_multiplier + + 0.00015800200344529003 + 0 + 0 + 1 + + distance_multiplier + + 1 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000474974513 + -0.33000001311302185 + 1 + + haze_density + + 1.2300000190734863 + 0 + 0 + 1 + + haze_horizon + + 0.17999999225139618 + 0.13210900127887726 + 0.13210900127887726 + 1 + + lightnorm + + -0 + 0.99785882234573364 + 0.06540437787771225 + 0 + + max_y + + 600 + 0 + 0 + 1 + + preset_num + 3 + star_brightness + 0 + sun_angle + 1.5053452253341675 + sunlight_color + + 3 + 3 + 3 + 1 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion.xml new file mode 100644 index 000000000..0c318b6ed --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Light%20Explosion.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.31640625 + 0.31640625 + 0.31640625 + 0.10546875 + + blue_density + + 0.40625 + 0.59642577171325684 + 0.63374996185302734 + 0.31687498092651367 + + blue_horizon + + 0.296875 + 0.296875 + 0.296875 + 0.1484375 + + cloud_color + + 0 + 0 + 0 + 0 + + cloud_pos_density1 + + 0.57999998331069946 + 0.44999998807907104 + 0.91999995708465576 + 1 + + cloud_pos_density2 + + 1.6884100437164307 + 0.52609699964523315 + 0.15999999642372131 + 1 + + cloud_scale + + 0.5899999737739563 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.649999618530273 + 10.359999656677246 + + cloud_shadow + + 0.32999998331069946 + 0 + 0 + 1 + + density_multiplier + + 0.00016999999934341758 + 0 + 0 + 1 + + distance_multiplier + + 31.30000114440918 + 0 + 0 + 1 + + east_angle + 2.4504423141479492 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1.0499999523162842 + 0 + 0 + 1 + + glow + + 0.19999980926513672 + 0.0010000000474974513 + -2.5 + 1 + + haze_density + + 0 + 0 + 0 + 1 + + haze_horizon + + 0 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.59898221492767334 + 0.34202155470848083 + 0.72404521703720093 + 0 + + max_y + + 827 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.7925252914428711 + sunlight_color + + 0.77144533395767212 + 0.86230051517486572 + 0.9685547947883606 + 0.32285159826278687 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Optimal%20Skin%20%28no%20shadows%29.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Optimal%20Skin%20%28no%20shadows%29.xml new file mode 100644 index 000000000..3ca73ade7 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Optimal%20Skin%20%28no%20shadows%29.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.7898764610290527 + 1.7999999523162842 + 1.7999999523162842 + 0.59999996423721313 + + blue_density + + 0.17840505994550426 + 0.41624023325357484 + 0.78666704491092787 + 0.79331876337528229 + + blue_horizon + + 0.23695440116756572 + 0.30749256375219147 + 0.38000917434692383 + 0.77331733703613281 + + cloud_color + + 0.28744032449850465 + 0.28744032449850465 + 0.28744032449850465 + 0.80331945419311523 + + cloud_pos_density1 + + 1 + 1 + 0.92000281969717435 + 1 + + cloud_pos_density2 + + 0.29999998211860657 + 0.29999998211860657 + 0.125 + 1 + + cloud_scale + + 0.4199999912582939 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.399593049491159 + 10.010999738700775 + + cloud_shadow + + 0.39999997615814209 + 0 + 0 + 1 + + density_multiplier + + 0.0003666600819003385 + 0 + 0 + 1 + + distance_multiplier + + 0.93332862854003906 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 5 + 0.0010000000491261485 + -0.47999999284769501 + 1 + + haze_density + + 0.69999999205299446 + 0 + 0 + 1 + + haze_horizon + + 0.17000070333489248 + 0.1991559977192594 + 0.1991559977192594 + 1 + + lightnorm + + 0 + 0.3070400059223175 + -0.95169663429260254 + 0 + + max_y + + 910.02454853057861 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 2.8295114040374756 + sunlight_color + + 0.29243719577789307 + 0.29459795355796814 + 0.29999998211860657 + 0.099999994039535522 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Realistic%20ambient.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Realistic%20ambient.xml new file mode 100644 index 000000000..506d8ae2c --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Realistic%20ambient.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.73828125 + 0.63248288631439209 + 0.61171871423721313 + 0.24609375 + + blue_density + + 1.0170860290527344 + 1.015869140625 + 1.171630859375 + 0.5858154296875 + + blue_horizon + + 0.287109375 + 0.39328005909919739 + 0.478515625 + 0.2392578125 + + cloud_color + + 0.50999999046325684 + 0.50999999046325684 + 0.50999999046325684 + 0.50999999046325684 + + cloud_pos_density1 + + 0.34999999403953552 + 0.31000000238418579 + 1 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.099999994039535522 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.49940013885498 + 10.01099967956543 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.00026000000070780516 + 0 + 0 + 1 + + distance_multiplier + + 6.0999999046325684 + 0 + 0 + 1 + + east_angle + 0.62831848859786987 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 8.2000017166137695 + 0.0010000000474974513 + -0.19999998807907104 + 1 + + haze_density + + 1.7799999713897705 + 0 + 0 + 1 + + haze_horizon + + 0.23999999463558197 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.47401577234268188 + 0.59131073951721191 + -0.65242677927017212 + 0 + + max_y + + 720 + 0 + 0 + 1 + + preset_num + 21 + star_brightness + 0 + sun_angle + 2.5089094638824463 + sunlight_color + + 1.677886962890625 + 1.646484375 + 1.728515625 + 0.576171875 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Red%20moments.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Red%20moments.xml new file mode 100644 index 000000000..cd7ba4933 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Red%20moments.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.73851561546325684 + 0.65285521745681763 + 0.64148437976837158 + 0.24617186188697815 + + blue_density + + 0.83835935592651367 + 1.0087707042694092 + 1.1216405630111694 + 0.56082028150558472 + + blue_horizon + + 0.387725830078125 + 0.24486541748046875 + 0.159149169921875 + 0.1938629150390625 + + cloud_color + + 0.9296875 + 0.9296875 + 0.9296875 + 0.9296875 + + cloud_pos_density1 + + 0.47999998927116394 + 0.5 + 1 + 1 + + cloud_pos_density2 + + 0.53999996185302734 + 0.4699999988079071 + 0.059999998658895493 + 1 + + cloud_scale + + 0.3099999725818634 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.49940013885498 + 10.01099967956543 + + cloud_shadow + + 0.29999998211860657 + 0 + 0 + 1 + + density_multiplier + + 0.00026999998954124749 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 2.6389377117156982 + enable_cloud_scroll + + 0 + 0 + + gamma + + 0.74000000953674316 + 0 + 0 + 1 + + glow + + 5.5999994277954102 + 0.0010000000474974513 + -0.29999998211860657 + 1 + + haze_density + + 1.8899999856948853 + 0 + 0 + 1 + + haze_horizon + + 0.26999998092651367 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.44095441699028015 + 0.40274816751480103 + 0.80209296941757202 + 0 + + max_y + + 752 + 0 + 0 + 1 + + preset_num + 21 + star_brightness + 0 + sun_angle + 2.7270753383636475 + sunlight_color + + 1.4399999380111694 + 1.4399999380111694 + 1.4399999380111694 + 0.47999998927116394 + + + diff --git a/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Tan%20Skin.xml b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Tan%20Skin.xml new file mode 100644 index 000000000..3ac14b91a --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Annan%20Adored%20Tan%20Skin.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.53460693359375 + 1.2169990539550781 + 1.18414306640625 + 0.51153564453125 + + blue_density + + 1.5458984375 + 1.520721435546875 + 1.5166015625 + 0.77294921875 + + blue_horizon + + 0.5263671875 + 0.2863616943359375 + 0.2392578125 + 0.26318359375 + + cloud_color + + 0.50999999046325684 + 0.50999999046325684 + 0.50999999046325684 + 0.50999999046325684 + + cloud_pos_density1 + + 0.34999999403953552 + 0.31000000238418579 + 1 + 1 + + cloud_pos_density2 + + 0.5 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.099999994039535522 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.49940013885498 + 10.01099967956543 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.0002899999963119626 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 0 + enable_cloud_scroll + + 0 + 0 + + gamma + + 1 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -2.1499998569488525 + 1 + + haze_density + + 0 + 0 + 0 + 1 + + haze_horizon + + 0.23999999463558197 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0 + 0.5735776424407959 + -0.81915122270584106 + 0 + + max_y + + 560 + 0 + 0 + 1 + + preset_num + 21 + star_brightness + 0 + sun_angle + 2.5307259559631348 + sunlight_color + + 0.9609375 + 0.9609375 + 0.9609375 + 0.3203125 + + + diff --git a/indra/newview/app_settings/windlight/skies/Gwen%27s%20Light.xml b/indra/newview/app_settings/windlight/skies/Gwen%27s%20Light.xml new file mode 100644 index 000000000..beaa7bba2 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Gwen%27s%20Light.xml @@ -0,0 +1,141 @@ + + + ambient + + 1.2274205684661865 + 1.220489501953125 + 2.107635498046875 + 0.702545166015625 + + blue_density + + 0.625 + 0.625 + 0.625 + 0.3125 + + blue_horizon + + 0.025146491825580597 + 0.099853508174419403 + 0.050827037543058395 + 0.049926754087209702 + + cloud_color + + 0.54396075010299683 + 0.1728515625 + 0.8818359375 + 0.8818359375 + + cloud_pos_density1 + + 0.70999997854232788 + 0.53047597408294678 + 0.32999998331069946 + 1 + + cloud_pos_density2 + + 0.29999998211860657 + 0.29999998211860657 + 0.25 + 1 + + cloud_scale + + 0.25999999046325684 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.069999694824219 + 9.7899999618530273 + + cloud_shadow + + 0.43999999761581421 + 0 + 0 + 1 + + density_multiplier + + 0.00069000001531094313 + 0 + 0 + 1 + + distance_multiplier + + 10 + 0 + 0 + 1 + + east_angle + 2.3876104354858398 + enable_cloud_scroll + + 1 + 1 + + gamma + + 2 + 0 + 0 + 1 + + glow + + 1.6000008583068848 + 0.0010000000474974513 + -1.1999999284744263 + 1 + + haze_density + + 0.59999996423721313 + 0 + 0 + 1 + + haze_horizon + + 0.35999998450279236 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.56918007135391235 + 0.55557030439376831 + 0.60611522197723389 + 1 + + max_y + + 1493 + 0 + 0 + 1 + + preset_num + 2 + star_brightness + 2 + sun_angle + 5.6941366195678711 + sunlight_color + + 2.1550192832946777 + 1.8917083740234375 + 2.4442291259765625 + 0.8147430419921875 + + + diff --git a/indra/newview/app_settings/windlight/skies/Oceane%27s%20Body%20Designs.xml b/indra/newview/app_settings/windlight/skies/Oceane%27s%20Body%20Designs.xml new file mode 100644 index 000000000..1fec5b8af --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Oceane%27s%20Body%20Designs.xml @@ -0,0 +1,141 @@ + + + ambient + + 2.03999996185302734375 + 1.94999992847442626953125 + 1.83000004291534423828125 + 2.03999996185302734375 + + blue_density + + 0.63999998569488525390625 + 1.17999994754791259765625 + 2 + 2 + + blue_horizon + + 0.2199999988079071044921875 + 0.2199999988079071044921875 + 0.319999992847442626953125 + 0.319999992847442626953125 + + cloud_color + + 0.2261587334555770212318748 + 0.2261587334555770212318748 + 0.2261587334555770212318748 + 0.9999848079680475620989455 + + cloud_pos_density1 + + 0.884190976619720458984375 + 0.53047597408294677734375 + 0.8800030851365363560034893 + 1 + + cloud_pos_density2 + + 0.384193003177642822265625 + 0.5 + 0.125 + 1 + + cloud_scale + + 0.4199999868869781494140625 + 0 + 0 + 1 + + cloud_scroll_rate + + 10.49939242953405482694507 + 10.0109996795654296875 + + cloud_shadow + + 0.1599999964237213134765625 + 0 + 0 + 1 + + density_multiplier + + 0.0001799999881768599152565002 + 0 + 0 + 1 + + distance_multiplier + + 2 + 0 + 0 + 1 + + east_angle + 1.00530970096588134765625 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.2200000286102294921875 + 0 + 0 + 1 + + glow + + 5 + 0.001000000047497451305389404 + -0.449999988079071044921875 + 1 + + haze_density + + 0.680000007152557373046875 + 0 + 0 + 1 + + haze_horizon + + 0.08999999612569808959960938 + 0.1991560012102127075195312 + 0.1991560012102127075195312 + 1 + + lightnorm + + -0.8443279266357421875 + 0 + 0.535826742649078369140625 + 0 + + max_y + + 188 + 0 + 0 + 1 + + preset_num + 2 + star_brightness + 0 + sun_angle + 0 + sunlight_color + + 2.5799999237060546875 + 2.5799999237060546875 + 2.610000133514404296875 + 2.610000133514404296875 + + + diff --git a/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20clean.xml b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20clean.xml new file mode 100644 index 000000000..82dd68d2d --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20clean.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.89999997615814209 + 0.89999997615814209 + 0.89999997615814209 + 0.29999998211860657 + + blue_density + + 0.36974793672561646 + 0.38095235824584961 + 0.39999997615814209 + 0.19999998807907104 + + blue_horizon + + 0 + 0.042857140302658081 + 0.059999998658895493 + 0.029999999329447746 + + cloud_color + + 0.039999999105930328 + 0.056000009179115295 + 0.079999998211860657 + 0.079999998211860657 + + cloud_pos_density1 + + 0.40999999642372131 + 0.23999999463558197 + 0.099999994039535522 + 1 + + cloud_pos_density2 + + 0.32999998331069946 + 0.45999997854232788 + 0.11999999731779099 + 1 + + cloud_scale + + 0.25 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.7100000381469727 + 10.590000152587891 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.00014999999257270247 + 0 + 0 + 1 + + distance_multiplier + + 0 + 0 + 0 + 1 + + east_angle + 0.9424777626991272 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.5 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.48999997973442078 + 0 + 0 + 1 + + haze_horizon + + 0.049999997019767761 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.30724778771400452 + 0.92507719993591309 + -0.22322858870029449 + 0 + + max_y + + 188 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 1.9603538513183594 + sunlight_color + + 0.14999999105930328 + 0.14999999105930328 + 0.14999999105930328 + 0.049999997019767761 + + + diff --git a/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20extreme.xml b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20extreme.xml new file mode 100644 index 000000000..938d710f0 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20extreme.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.89999997615814209 + 0.89999997615814209 + 0.89999997615814209 + 0.29999998211860657 + + blue_density + + 0.36974793672561646 + 0.38095235824584961 + 0.39999997615814209 + 0.19999998807907104 + + blue_horizon + + 0 + 0.042857140302658081 + 0.059999998658895493 + 0.029999999329447746 + + cloud_color + + 0.039999999105930328 + 0.056000009179115295 + 0.079999998211860657 + 0.079999998211860657 + + cloud_pos_density1 + + 0.40999999642372131 + 0.23999999463558197 + 0.099999994039535522 + 1 + + cloud_pos_density2 + + 0.32999998331069946 + 0.45999997854232788 + 0.11999999731779099 + 1 + + cloud_scale + + 0.25 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.7100000381469727 + 10.590000152587891 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.0003499999875202775 + 0 + 0 + 1 + + distance_multiplier + + 60.0 + 0 + 0 + 1 + + east_angle + 0.9424777626991272 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.5 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.48999997973442078 + 0 + 0 + 1 + + haze_horizon + + 0.049999997019767761 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.30724778771400452 + 0.92507719993591309 + -0.22322858870029449 + 0 + + max_y + + 188 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 1.9603538513183594 + sunlight_color + + 0.14999999105930328 + 0.14999999105930328 + 0.14999999105930328 + 0.049999997019767761 + + + diff --git a/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20hard.xml b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20hard.xml new file mode 100644 index 000000000..f41ede37f --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20hard.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.89999997615814209 + 0.89999997615814209 + 0.89999997615814209 + 0.29999998211860657 + + blue_density + + 0.36974793672561646 + 0.38095235824584961 + 0.39999997615814209 + 0.19999998807907104 + + blue_horizon + + 0 + 0.042857140302658081 + 0.059999998658895493 + 0.029999999329447746 + + cloud_color + + 0.039999999105930328 + 0.056000009179115295 + 0.079999998211860657 + 0.079999998211860657 + + cloud_pos_density1 + + 0.40999999642372131 + 0.23999999463558197 + 0.099999994039535522 + 1 + + cloud_pos_density2 + + 0.32999998331069946 + 0.45999997854232788 + 0.11999999731779099 + 1 + + cloud_scale + + 0.25 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.7100000381469727 + 10.590000152587891 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.00025000001187436283 + 0 + 0 + 1 + + distance_multiplier + + 40.0 + 0 + 0 + 1 + + east_angle + 0.9424777626991272 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.5 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.48999997973442078 + 0 + 0 + 1 + + haze_horizon + + 0.049999997019767761 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.30724778771400452 + 0.92507719993591309 + -0.22322858870029449 + 0 + + max_y + + 188 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 1.9603538513183594 + sunlight_color + + 0.14999999105930328 + 0.14999999105930328 + 0.14999999105930328 + 0.049999997019767761 + + + diff --git a/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20soft.xml b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20soft.xml new file mode 100644 index 000000000..d15c8d009 --- /dev/null +++ b/indra/newview/app_settings/windlight/skies/Tron%20Legacy%20soft.xml @@ -0,0 +1,141 @@ + + + ambient + + 0.89999997615814209 + 0.89999997615814209 + 0.89999997615814209 + 0.29999998211860657 + + blue_density + + 0.36974793672561646 + 0.38095235824584961 + 0.39999997615814209 + 0.19999998807907104 + + blue_horizon + + 0 + 0.042857140302658081 + 0.059999998658895493 + 0.029999999329447746 + + cloud_color + + 0.039999999105930328 + 0.056000009179115295 + 0.079999998211860657 + 0.079999998211860657 + + cloud_pos_density1 + + 0.40999999642372131 + 0.23999999463558197 + 0.099999994039535522 + 1 + + cloud_pos_density2 + + 0.32999998331069946 + 0.45999997854232788 + 0.11999999731779099 + 1 + + cloud_scale + + 0.25 + 0 + 0 + 1 + + cloud_scroll_rate + + 9.7100000381469727 + 10.590000152587891 + + cloud_shadow + + 0.35999998450279236 + 0 + 0 + 1 + + density_multiplier + + 0.00014999999257270247 + 0 + 0 + 1 + + distance_multiplier + + 20.0 + 0 + 0 + 1 + + east_angle + 0.9424777626991272 + enable_cloud_scroll + + 1 + 1 + + gamma + + 1.5 + 0 + 0 + 1 + + glow + + 20 + 0.0010000000474974513 + -0 + 1 + + haze_density + + 0.48999997973442078 + 0 + 0 + 1 + + haze_horizon + + 0.049999997019767761 + 0.19915600121021271 + 0.19915600121021271 + 1 + + lightnorm + + 0.30724778771400452 + 0.92507719993591309 + -0.22322858870029449 + 0 + + max_y + + 188 + 0 + 0 + 1 + + preset_num + 22 + star_brightness + 0 + sun_angle + 1.9603538513183594 + sunlight_color + + 0.14999999105930328 + 0.14999999105930328 + 0.14999999105930328 + 0.049999997019767761 + + + diff --git a/indra/newview/app_settings/windlight/skies/wastelands.xml b/indra/newview/app_settings/windlight/skies/wastelands.xml index 313111fbb..33ae9e562 100644 --- a/indra/newview/app_settings/windlight/skies/wastelands.xml +++ b/indra/newview/app_settings/windlight/skies/wastelands.xml @@ -2,7 +2,7 @@ ambient - 0.29999998211860657 + 0.29999998211860657 0.17999999225139618 0 0.29999998211860657 From 858bb15a6b45b02f0d8be8d11fb6860617b6790a Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Fri, 12 Oct 2012 12:36:31 -0400 Subject: [PATCH 123/123] Fixed issue --- indra/newview/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b9449ed62..480abaa27 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1272,8 +1272,6 @@ if (WINDOWS) if (INTEL_MEMOPS_LIBRARY) list(APPEND viewer_LIBRARIES ${INTEL_MEMOPS_LIBRARY}) endif (INTEL_MEMOPS_LIBRARY) - - use_prebuilt_binary(dbghelp) endif (WINDOWS) # Add the xui files. This is handy for searching for xui elements